An annotation class is declared with annotation class Name(...), and applied with @Name above a declaration, similar to Java's syntax. The genuinely distinctive complication: a Kotlin property in a primary constructor can simultaneously be a constructor parameter, a backing field, and a getter (plus a setter, if it's a var) — several different underlying JVM elements from one Kotlin declaration. An annotation meant for just one of those needs an explicit use-site target prefix (@get:JvmName, @field:Transient, @param:Positive, @property:...), or Kotlin applies a default target that may not be the one actually intended.
Declaring and applying a basic annotation
An annotation class can accept parameters, just like a regular class constructor — here, description is required whenever @Todo is applied.
annotation class Todo(val description: String)
class Report {
@Todo("Add input validation")
fun generate() { /* ... */ }
}The real complication: one property, several JVM elements
Without a use-site target, Kotlin picks a reasonable default for where an annotation attaches — but that default isn't always the element the annotation was actually meant for, which is exactly why use-site targets exist.
class User(val name: String) {
// 'name' is simultaneously: a constructor parameter, a backing field, AND a getter —
// an annotation here needs to specify WHICH of those it targets
}Use-site targets: specifying exactly which element
@get:, @set:, @field:, and @param: each pin the annotation to one specific underlying JVM element — essential when integrating with Java frameworks (like Jackson or JPA) that expect an annotation on a very specific element, such as the field or the getter specifically.
class User(
@get:JvmName("getUserName") val name: String, // targets the GETTER specifically
@field:Transient val temporaryToken: String // targets the FIELD specifically
)