When you annotate a Kotlin property without a use-site target, what is the default resolution order the compiler uses to choose the target?
answer
- Order: param → property → field
- First permitted target wins, only one chosen
- get/set/receiver/delegate never auto-selected
- PROPERTY target is Kotlin-only, invisible to Java
- Ctor property ⇒ param often wins
basics
~10 sKotlin tries targets in a fixed order and uses the first one the annotation is allowed on: the constructor parameter, then the property, then the field. Getters and setters are never chosen automatically.
solid answer
~40 sIf no use-site target is given, Kotlin walks a fixed priority list and applies the annotation to the **first** target that the annotation's own `@Target` permits. The order is: `param`, then `property`, then `field`. The compiler picks exactly one (not all that match). Crucially, `get`/`set`/`receiver`/`delegate` are **never** selected by default — you must request them explicitly. So if an annotation declares `@Target(VALUE_PARAMETER, FIELD)` and you place it on a primary-constructor property, it lands on the parameter, because `param` comes first. If it only allows `FIELD`, it lands on the field. The `property` target is Kotlin-only metadata (invisible to Java reflection), so an annotation that resolves to `property` won't be seen by Java libraries — a common interop surprise.
code
kotlin · 8 lines@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
annotation class Marker
class User(@Marker val name: String) // -> VALUE_PARAMETER (param outranks field)
class Order {
@Marker var total: Long = 0 // no ctor param here -> FIELD
}go deeper
Knows the default isn't random but may not recall the exact order.
States param → property → field and that only one target is chosen.
Explains the PROPERTY-is-Kotlin-only interop trap and why getters need explicit targeting.
Sets conventions to always be explicit on interop boundaries to avoid metadata-vs-bytecode surprises.
## The default order When you write an annotation on a property **without** a `@get:`/`@field:`/etc. prefix, the Kotlin compiler does not guess. It evaluates a fixed list of candidate targets and applies the annotation to the **first** candidate that the annotation actually allows (per its `@Target` meta-annotation): 1. **`param`** — the constructor parameter (only exists for primary-constructor properties) 2. **`property`** — the Kotlin property (a Kotlin-only target stored in metadata) 3. **`field`** — the backing field The compiler chooses **exactly one** — the first match wins; it does not apply to all matching targets. ## What is never chosen automatically `get`, `set`, `receiver`, and `delegate` are **never** selected by the default rule. If you want the annotation on the getter or setter, you must write `@get:` / `@set:` explicitly. This is the root cause of "my getter annotation does nothing" bugs: the default never reaches the getter. ## Worked examples ```kotlin @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD) annotation class Tag class A(@Tag val x: Int) // resolves to PARAM (param beats field) class B { @Tag val y: Int = 0 // not a ctor property -> param skipped -> resolves to FIELD } ``` - In `A`, `x` is a constructor property, so `param` is a candidate and wins. - In `B`, `y` is declared in the body, so there is no parameter; `property` isn't in `@Target`, so it falls through to `field`. ## The interop trap with `property` `AnnotationTarget.PROPERTY` is a **Kotlin-only** concept. Annotations stored there live in Kotlin metadata and are **invisible to Java reflection**. So if an annotation only permits `PROPERTY` (and you don't override), Java libraries like Jackson or Hibernate won't see it. To make such an annotation reachable from Java, you typically force `@field:` or `@get:`. ## Practical guidance - For JVM-interop annotations, **be explicit** rather than relying on the default. - Remember the mnemonic order: **param → property → field**. - Getters/setters need an explicit prefix, always.
- Why won't an annotation that resolves to the `property` target show up in Java reflection?AnnotationTarget.PROPERTY is a Kotlin-only target stored in the class metadata, not as a real JVM annotation on a field or method, so java.lang.reflect can't see it.
- Does the default ever target the getter?No. get/set are never chosen by the default order; you must write @get: or @set: explicitly.
saying these in an interview costs you the question
- Saying the default applies the annotation to all matching targets
- Claiming the order is field → property → param (reversed)
- Believing getters can be auto-targeted by default
- Not knowing PROPERTY is invisible to Java reflection