skip to content

@Retention & @Target

@Retention decides whether an annotation survives to runtime and @Target restricts where it may be applied. If your reflective lookup finds nothing, the retention is usually the reason.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What do the @Retention and @Target meta-annotations control when you declare your own Kotlin annotation?

level: juniorimportance: must knowfreq 55%

answer

  1. Retention = how long it lives (SOURCE/BINARY/RUNTIME)
  2. Target = where it can go (CLASS/FUNCTION/PROPERTY/...)
  3. Default retention = RUNTIME
  4. Only RUNTIME is reflection-visible
  5. Wrong target = compile error

basics

~10 s

@Retention says how long the annotation is kept: source only, in the class file, or readable at runtime. @Target says where you can put it: on classes, functions, properties, parameters, and so on.

solid answer

~30 s

Both are meta-annotations (annotations applied to an annotation class). @Retention takes an AnnotationRetention enum value: SOURCE (discarded by the compiler), BINARY (kept in the .class file but not visible to reflection), or RUNTIME (kept and readable via Kotlin/Java reflection). Default is RUNTIME. @Target lists the allowed code elements via AnnotationTarget values such as CLASS, FUNCTION, PROPERTY, FIELD, VALUE_PARAMETER, CONSTRUCTOR, EXPRESSION, TYPE. If you omit @Target, the annotation may be applied to most declarations. Putting an annotation on a disallowed element is a compile error. Note that EXPRESSION targets require SOURCE retention because expressions aren't kept in bytecode.

code

kotlin · 8 lines
kotlin
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Audited

@Audited
class Service {
    @Audited fun run() {}
}

go deeper

for a junior

Knows Retention = lifetime, Target = location, and can name a few AnnotationTarget values.

for a middle

Recalls that RUNTIME is the default and only RUNTIME is reflection-visible; knows wrong target is a compile error.

for a senior

Explains the SOURCE/BINARY/RUNTIME tradeoffs and why EXPRESSION needs SOURCE retention.

for a principal

Frames retention/target choices in terms of API design, tooling integration, and binary-compatibility impact.

## Meta-annotations A **meta-annotation** is an annotation you place on an `annotation class` declaration to configure how that annotation behaves. `@Retention` and `@Target` are the two most important ones. ## @Retention — how long the annotation lives `@Retention` takes one value from the `AnnotationRetention` enum: - **SOURCE** — kept only during compilation, then thrown away. Useful for compiler hints / lint / annotation-processor markers that don't need to exist at runtime (e.g. `@Suppress`). - **BINARY** — stored in the compiled `.class` file (bytecode) but **not** visible to reflection at runtime. - **RUNTIME** — stored in the class file **and** readable at runtime through reflection (`kotlin.reflect` or Java reflection). This is the **default** if you don't specify `@Retention`. Only **RUNTIME** annotations can be read back with reflection such as `kClass.annotations` or `findAnnotation<T>()`. ## @Target — where the annotation may appear `@Target` takes one or more `AnnotationTarget` enum values. Common ones: - `CLASS` (classes, interfaces, objects, annotation classes) - `FUNCTION`, `PROPERTY`, `FIELD`, `LOCAL_VARIABLE` - `VALUE_PARAMETER` (function/constructor parameters) - `CONSTRUCTOR`, `PROPERTY_GETTER`, `PROPERTY_SETTER` - `TYPE`, `TYPE_PARAMETER`, `EXPRESSION`, `FILE`, `ANNOTATION_CLASS` If you apply the annotation to an element not in the list, the compiler raises an error: *"This annotation is not applicable to target ..."*. If you omit `@Target` entirely, the annotation defaults to being usable on most declaration sites (but not expressions/types). ## Example ```kotlin @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) annotation class Audited @Audited // OK: class class Service { @Audited // OK: function fun run() {} } // @Audited val x = 1 // compile error: not applicable to PROPERTY ``` ## Key constraints - `EXPRESSION` and `TYPE`-style targets generally require **SOURCE** retention, because expressions and most type usages aren't preserved in bytecode. - Defaults: retention defaults to **RUNTIME**; target defaults to a broad set of declarations.

  • If you don't specify @Retention, what do you get?
    RUNTIME retention by default, so the annotation is readable via reflection.
  • What happens if you put an annotation on a target not listed in @Target?
    A compile error: the annotation is not applicable to that element.

Retention is the shelf-life of a label; Target is the list of jars the label is allowed to be stuck on.

saying these in an interview costs you the question

  • Saying the default retention is SOURCE (it's RUNTIME)
  • Claiming @Target controls runtime visibility (that's @Retention)
  • Thinking SOURCE annotations are readable by reflection
  • Confusing meta-annotations with use-site targets like @get:/@field:

context

open as a page

Explain the difference between SOURCE, BINARY, and RUNTIME retention, and which one you must use to read an annotation via reflection.

level: middleimportance: must knowfreq 50%

basics

~10 s

SOURCE is dropped after compiling, BINARY stays in the class file but reflection can't see it, RUNTIME stays and reflection can read it. You need RUNTIME for reflection.

open as a page

How does @Target restrict where an annotation can be applied, and what happens if you apply it to a disallowed element? Name several AnnotationTarget values.

level: middleimportance: should knowfreq 42%

basics

~10 s

@Target lists the kinds of code elements (class, function, property, parameter, etc.) the annotation is allowed on. If you put it somewhere not in that list, the code won't compile.

open as a page

You are designing a validation annotation that a runtime framework will read on data-class properties via reflection. What @Retention and @Target would you choose, and why does the property/field/getter distinction matter?

level: seniorimportance: should knowfreq 33%

basics

~20 s

Use RUNTIME retention so reflection can read it, and target the property (and maybe field/parameter). Because a property, its field, and its getter are separate places, you must make sure the annotation lands where your framework actually looks.

open as a page

How do Kotlin's retention defaults and AnnotationRetention map to Java's @Retention/RetentionPolicy, and what interop gotcha follows from the differing defaults?

level: seniorimportance: nice to knowfreq 20%

basics

~10 s

Kotlin's SOURCE/BINARY/RUNTIME line up with Java's SOURCE/CLASS/RUNTIME. The catch: Kotlin defaults to RUNTIME while Java defaults to CLASS (BINARY), so a Kotlin annotation with no @Retention is reflection-visible but a Java one isn't.

open as a page