skip to content

Use-Site Targets (@get:/@param:/@field:)

A property becomes several JVM elements, so @get:, @field:, and @param: choose which one the annotation lands on. This is the single most common cause of a validation or persistence annotation quietly doing nothing in Kotlin.

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

questions

5

What is a use-site target on a Kotlin annotation (e.g. @get:, @field:, @param:), and why is it needed when annotating a property?

level: juniorimportance: must knowfreq 70%

answer

  1. One property = field + getter + setter + ctor param
  2. Prefix before the colon: @get:/@field:/@param:
  3. Wrong target = annotation silently ignored
  4. Targets: get/set/field/param/property/receiver/delegate

basics

~20 s

One Kotlin property can become several Java pieces: a field, a getter, a setter, a constructor parameter. A use-site target like @get: or @field: tells the compiler which of those pieces the annotation should land on.

solid answer

~40 s

A property declaration in Kotlin generates multiple JVM elements: a backing field, a getter, optionally a setter, and (for primary-constructor properties) a constructor parameter. An annotation written on the property is ambiguous about which element it targets, so Kotlin lets you disambiguate with a use-site target prefix: `@get:`, `@set:`, `@field:`, `@param:`, `@property:`, `@receiver:`, and `@delegate:`. For example `@field:JsonProperty("id")` puts the annotation on the Java field, while `@get:JsonProperty("id")` puts it on the getter. This matters because libraries that use reflection (Jackson, JPA, validation) inspect a specific element; targeting the wrong one means the annotation is silently ignored. If you omit the target, Kotlin applies a default resolution order based on the annotation's `@Target`.

code

kotlin · 5 lines
kotlin
class Account(
    @field:Id val id: Long,                 // annotation on the field
    @get:JsonProperty("user_name") val name: String, // on the getter
    @param:Positive val balance: Long,      // on the ctor parameter
)

go deeper

for a junior

Knows a property maps to multiple JVM elements and that @get:/@field:/@param: choose one.

for a middle

Can predict which library reads which element and fix a silently-ignored annotation.

for a senior

Explains the default resolution order and the full target set including @property:/@delegate:.

for a principal

Frames it as a Kotlin-to-JVM impedance issue and sets team conventions for interop annotations.

## The problem use-site targets solve In Kotlin, a single line like `val id: Long` is more than a field. When compiled to JVM bytecode, a property can produce **several distinct elements**: - a **backing field** (the actual storage slot) - a **getter** method - a **setter** method (only for `var`) - a **constructor parameter** (only when the property is declared in the primary constructor) If you write `@SomeAnnotation val id: Long`, the compiler must decide which of these the annotation belongs to. A **use-site target** is a prefix before the colon that removes the ambiguity. ## The available targets ```kotlin class User( @param:NotBlank val name: String, // the constructor parameter @field:Id val id: Long, // the backing field @get:JsonProperty("email") val email: String, // the getter ) ``` The full set: - `@get:` — the property getter - `@set:` — the property setter (only valid on `var`) - `@field:` — the backing field - `@param:` — the primary-constructor parameter - `@property:` — the Kotlin property itself (a Kotlin-only target, stored in metadata) - `@receiver:` — the receiver parameter of an extension function/property - `@delegate:` — the field that stores a delegate instance (`by`) ## Why it matters in practice Many Java libraries read annotations **reflectively** off a *specific* element. Jackson and JPA often look at fields **or** getters; Bean Validation may look at the field, the getter, or the constructor parameter depending on configuration. If your annotation lands on the wrong element, it is simply ignored — no error, just silently wrong behavior. That makes the wrong-target bug hard to spot. ## Default behavior If you don't specify a target, Kotlin doesn't pick randomly — it follows a fixed **default resolution order** based on which targets the annotation declares in its `@Target`. (Covered in depth in a separate question.)

  • What happens if you put an annotation on a property without any use-site target?
    Kotlin applies the default resolution order: it picks the first applicable target from the list param, property, field (skipping ones not allowed by the annotation's @Target).
  • Why might a Jackson @JsonProperty annotation appear to do nothing?
    It probably landed on the wrong element. If Jackson reads getters but the annotation defaulted onto the field (or vice versa), it is ignored. Adding @get: or @field: fixes it.

Like addressing a letter to one specific person in a shared household — without the name, the mail goes to whoever the default rule says.

saying these in an interview costs you the question

  • Thinking a Kotlin property is just a field
  • Claiming the target prefix is optional cosmetic syntax with no effect
  • Saying a wrong target causes a compile error (it usually doesn't)
  • Confusing use-site targets with the annotation's own @Target meta-annotation

context

open as a page

When you annotate a Kotlin property without a use-site target, what is the default resolution order the compiler uses to choose the target?

level: middleimportance: must knowfreq 55%

basics

~10 s

Kotlin 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.

open as a page

You annotate a Kotlin data class property with a Jackson/JPA annotation and it seems ignored at runtime. How do use-site targets explain and fix this?

level: middleimportance: should knowfreq 60%

basics

~20 s

The annotation probably landed on the wrong JVM element. The library reads, say, the getter, but Kotlin put your annotation on the field or the constructor parameter. Adding the right prefix like @get: or @field: makes it visible.

open as a page

Explain the @delegate: and @receiver: use-site targets. When would you reach for each, and how do they differ from @field:?

level: seniorimportance: should knowfreq 30%

basics

~10 s

@delegate: annotates the hidden field that stores a delegate created with by. @receiver: annotates the receiver of an extension function or property. Both target elements that @field: cannot reach.

open as a page

Contrast @property: with @field:. What does each produce at the bytecode/metadata level, and what are the consequences for Java reflection and for annotations declared with only @Target(PROPERTY)?

level: seniorimportance: nice to knowfreq 22%

basics

~10 s

@field: puts the annotation on the real Java field, so Java reflection can read it. @property: stores it in Kotlin-only metadata, so plain Java reflection can't see it — only Kotlin reflection can.

open as a page