In Kotlin, what is a use-site annotation target, and why might you write `@field:NotNull` instead of just `@NotNull` on a property?
answer
- One property -> field + getter + setter + ctor param
- Default order: param > property > field (first match wins)
- @field: for JPA/Jackson reflection
- Bare annotation can silently miss the element a framework reads
- @property: is Kotlin-only metadata
basics
~10 sA single Kotlin property turns into several Java elements (field, getter, setter, constructor parameter). A use-site target like @field: tells the compiler which of those elements the annotation should land on.
solid answer
~40 sWhen you declare a Kotlin property, the compiler may emit a backing field, a getter, a setter, and (for primary-constructor properties) a constructor parameter. An annotation written with no prefix goes to a default element chosen by the compiler from the annotation's allowed @Target list. A use-site target — `@field:`, `@get:`, `@set:`, `@param:`, `@receiver:`, `@property:`, `@setparam:`, `@delegate:` — explicitly directs the annotation to one specific emitted element. `@field:NotNull` forces the annotation onto the backing field (what JPA/Jackson reflection often inspects), whereas `@get:NotNull` puts it on the getter. Choosing the wrong one means the framework looks at an element that has no annotation, so the constraint silently does nothing.
code
kotlin · 4 linesdata class Account(
@field:JsonProperty("acct_id") val id: Long,
@get:JsonProperty("display") val name: String,
)go deeper
Can state that one property maps to several JVM elements and that the target prefix selects one.
Knows the default priority order (param > property > field) and that the first match wins, so a bare annotation can miss the field.
Connects target choice to concrete framework reflection (JPA/Jackson/Bean Validation) and predicts silent failures.
Sets team conventions (e.g. always @field: for persistence DTOs) and reasons about cross-framework portability of annotation placement.
## Why targets exist A Kotlin *property* is a single source-level declaration, but on the JVM it expands into **multiple bytecode elements**: - a **backing field** (private, holds the value) - a **getter** (`getX()`) - a **setter** (`setX()`, only for `var`) - a **constructor parameter** (only for properties declared in the primary constructor) When you put an annotation on a property, the compiler has to decide *which* of those elements receives it. A **use-site target** is the `target:` prefix you write before the annotation name to make that choice explicit. ## The available targets - `@field:` — the backing field - `@get:` — the getter method - `@set:` — the setter method - `@param:` — the primary-constructor parameter - `@property:` — the Kotlin property itself (Kotlin-only metadata, invisible to Java) - `@setparam:` — the parameter of the setter - `@receiver:` — the receiver of an extension function/property - `@delegate:` — the field storing a delegate instance (for `by`) ## Default resolution when you omit the target If you write a bare `@Ann`, the compiler picks **one** target from this priority order, using the annotation's own `@Target` declaration to filter: 1. `param` (constructor parameter, if applicable) 2. `property` 3. `field` The **first** applicable one wins — not all of them. So a bare annotation that is valid on both `param` and `field` lands on `param` only. ```kotlin import jakarta.validation.constraints.NotBlank data class User( // bare: lands on the constructor PARAMETER (first applicable) @NotBlank val name: String, // explicit: lands on the backing FIELD, which reflection scans @field:NotBlank val email: String, ) ``` ## Why it matters for Java/frameworks Many Java frameworks read annotations via reflection on a **specific** element. JPA, Jackson, and Hibernate Validator often scan the **field**; Bean Validation may scan the getter. If your annotation lands on the constructor parameter but the framework reads the field, the annotation is effectively invisible and the rule never fires — a silent failure with no compile error.
- If you write a bare `@NotBlank` on a primary-constructor `val`, which element gets it?The constructor parameter (`param`), because it is first in the default priority order and the annotation is applicable there.
- Does `@property:` make the annotation visible to Java?No. `@property:` stores it as Kotlin metadata only; plain Java reflection on the field/getter won't see it.
A property is like an address with several mailboxes (field, getter, setter); the use-site target is the apartment number that tells the postman exactly which box to drop the letter in.
saying these in an interview costs you the question
- Thinking a property is a single Java element
- Assuming a bare annotation lands on all elements at once
- Believing the default target is always the field
- Not knowing why a validation/JSON annotation silently does nothing