skip to content

What does @Repeatable do, and how do you declare and apply a repeatable annotation in Kotlin?

level: middleimportance: should knowfreq 45%

answer

  1. @Repeatable = same annotation more than once
  2. No hand-written container needed in modern Kotlin
  3. Compiler synthesizes the container
  4. Use findAnnotations<T>() to read all occurrences
  5. Need RUNTIME retention to reflect

basics

~10 s

@Repeatable lets you put the same annotation on one declaration more than once. You mark the annotation class with @Repeatable, then apply it multiple times.

solid answer

~40 s

Mark the annotation class with `@Repeatable` to allow the same annotation to appear multiple times on a single target. Apply it by simply repeating it: `@Tag("a") @Tag("b") class Foo`. In modern Kotlin you do *not* need to hand-write a container annotation — the compiler synthesizes one automatically. A repeatable annotation must have `RUNTIME` retention if you want to read all occurrences reflectively; the default retention is already `RUNTIME`. On the JVM, repeatable annotations are stored inside a generated container annotation (mirroring Java's `@Repeatable(Container.class)` mechanism), so reflectively you may need `KClass.annotations` plus container handling. Kotlin's reflection helper `findAnnotations()` flattens repeated occurrences for you.

code

kotlin · 10 lines
kotlin
@Repeatable
@Retention(AnnotationRetention.RUNTIME)
annotation class Tag(val value: String)

@Tag("a")
@Tag("b")
class Widget

import kotlin.reflect.full.findAnnotations
val all = Widget::class.findAnnotations<Tag>() // [Tag(a), Tag(b)]

go deeper

for a junior

Knows @Repeatable allows applying the same annotation multiple times and can show the syntax.

for a middle

Knows no manual container is needed, that retention must be RUNTIME to reflect, and how to read occurrences.

for a senior

Explains the JVM container synthesis and uses findAnnotations vs raw annotations correctly; notes Java interop.

for a principal

Weighs repeatable annotations vs a single annotation with a list parameter as an API design choice, considering processability and clarity.

## The problem @Repeatable solves Normally you may apply a given annotation **once** per declaration. Applying it twice is a compile error. `@Repeatable` lifts that restriction so the same annotation can be applied multiple times to one element — useful for things like multiple `@Role`, `@Tag`, or `@Schedule` markers. ## Declaring ```kotlin @Repeatable @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS) annotation class Tag(val value: String) ``` `@Repeatable` is itself a meta-annotation from `kotlin.annotation`. In current Kotlin (1.6+ and all 2.x) you simply add `@Repeatable` and the compiler does the rest — **no manually written container annotation is required**, unlike older Java where you had to declare a separate `@interface TagContainer`. ## Applying ```kotlin @Tag("payments") @Tag("audited") class PaymentService ``` Both `@Tag` instances are attached to `PaymentService`. ## Reading them back For reflection to see them at runtime the retention must be `RUNTIME` (which is the default). The JVM stores repeated annotations inside a synthesized **container** annotation. Kotlin reflection gives you a clean reader: ```kotlin import kotlin.reflect.full.findAnnotations val tags = PaymentService::class.findAnnotations<Tag>() // returns List<Tag> with both "payments" and "audited" ``` `findAnnotations<T>()` flattens the container and returns every occurrence. Plain `KClass.annotations` may instead show the synthesized container on the JVM, so prefer `findAnnotations` for repeatables. ## Constraints & gotchas - All repeated instances on the same element must be the same annotation type. - `@Repeatable` interacts with retention: choose `RUNTIME` to read them reflectively. - For Java interop the Kotlin compiler emits a Java-style container so Java code and Java reflection still work.

  • Do you still have to declare a container annotation like in old Java?
    No. Modern Kotlin synthesizes the container automatically when you mark the annotation @Repeatable. You write only the @Repeatable annotation itself.
  • Why might KClass.annotations not show both @Tag occurrences directly?
    On the JVM repeatables are wrapped in a generated container annotation, so the raw annotations list may show the container. Use kotlin.reflect.full.findAnnotations<T>(), which unwraps the container and returns each occurrence.

Like sticking several sticky notes on the same page instead of being limited to one.

saying these in an interview costs you the question

  • Claiming you must manually write a container @interface in Kotlin
  • Saying any annotation can be applied twice without @Repeatable
  • Forgetting that RUNTIME retention is needed to read them via reflection
  • Mixing different annotation types and calling that 'repeatable'
  • Not knowing findAnnotations flattens the container

context