Annotations and Reflection
|
This section documents Kotlin 2.4.x on the JVM, as published at kotlinlang.org, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against kotlinlang.org before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Annotations and reflection in Kotlin build directly on the JVM’s own annotation and reflection facilities, with
a Kotlin-specific reflection API (kotlin.reflect) layered on top for Kotlin-only concepts (properties,
nullability, data classes).
Declaring an Annotation
An annotation class carries no body of its own — only constructor parameters, which become the annotation’s attributes:
annotation class Author(val name: String)
@Author(name = "Ada Lovelace")
class Calculator
Meta-Annotations: @Target and @Retention
Two built-in meta-annotations control where a custom annotation may be applied and how long it survives:
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class Experimental(val reason: String)
class Api {
@Experimental("subject to change before 1.0")
fun betaFeature() { /* ... */ }
}
@Target restricts which declarations (classes, functions, properties, parameters, …) the annotation may
decorate; @Retention mirrors Java’s RetentionPolicy — SOURCE (compiler-only), BINARY (kept in the
.class file but invisible to reflection), or RUNTIME (readable via reflection, the default).
Using Built-In Annotations
Several annotations recur throughout ordinary Kotlin code, most already seen elsewhere in this section:
@Deprecated("use newMethod() instead", ReplaceWith("newMethod()"))
fun oldMethod() { }
@JvmStatic // exposes a companion-object function as a real "static" for Java callers
companion object {
@JvmStatic
fun create(): Widget = Widget()
}
@Suppress("UNCHECKED_CAST")
fun <T> unsafeCast(value: Any): T = value as T
@JvmStatic, @JvmOverloads, and @JvmName are a family of Java-interop annotations specific to Kotlin:
because Kotlin has no static keyword and supports default arguments (see
Functions), Java callers would otherwise not see a plain static method
or a set of overloads for each default-argument combination — these annotations ask the compiler to generate
that extra, Java-friendly surface.
Basic kotlin.reflect
::class produces a KClass<T> — Kotlin’s own reflection handle, richer than Java’s Class<T> for
Kotlin-specific constructs (nullable types, properties as first-class members, primary-constructor parameters):
class User(val name: String, val age: Int)
val kClass = User::class // KClass<User>
println(kClass.simpleName) // "User"
val javaClass = User::class.java // the underlying java.lang.Class<User>, for Java-facing APIs
val instance = User("Ada", 30)
for (prop in User::class.members) {
println("${prop.name}") // reflects over Kotlin members, including properties
}
::class.java is the bridge back to java.lang.Class whenever a Java API (a JSON library, a dependency
injection framework) expects one — KClass itself is not a drop-in replacement for Class at every call site.
See Also
-
Functions — default arguments, the feature
@JvmOverloadsexists to bridge to Java. -
Testing — how
@Test/@ExtendWithare themselves discovered via runtime-retained annotations and reflection. -
Kotlin and the JVM — the Java-interop annotations in the wider context of calling Kotlin from Java.
References
-
Kotlin docs — Calling Kotlin from Java (
@JvmStatic,@JvmOverloads,@JvmName).