What you'll learn
Annotations attach metadata to code, and reflection lets a program inspect itself at runtime. Together they power the frameworks you'll use every day — test runners, JSON libraries, dependency injection.
By the end you'll be able to:
- Use built-in annotations and create your own
- Control annotations with
@Targetand@Retention - Inspect types at runtime with reflection (
::class) - Understand how frameworks combine the two
Annotations
An annotation is metadata — a note on your code that the compiler, tools, or a framework can read. You've seen a few already (@Override isn't needed in Kotlin, but @Deprecated, @JvmStatic, and @Test are common):
open class Base {
open fun greet() = "hi"
}
class Child : Base() {
override fun greet() = "hey"
@Deprecated("Use greet() instead") // warns callers this is obsolete
fun oldGreet() = "hello"
}
println(Child().greet())Custom annotations
You define your own with the annotation class keyword. Two meta-annotations configure it: @Target says where it can be applied (functions, classes, properties…), and @Retention says how long it survives — crucially, RUNTIME is required for it to be visible to reflection:
@Target(AnnotationTarget.FUNCTION) // may annotate functions
@Retention(AnnotationRetention.RUNTIME) // keep it at runtime for reflection
annotation class Test
class MathTests {
@Test fun addsCorrectly() { }
@Test fun subtractsCorrectly() { }
}Note
@Retention(AnnotationRetention.RUNTIME). That's how a test runner finds every function marked @Test and runs it.Reflection
Reflection lets code examine itself at runtime — a type's name, its members, its annotations. Every value gives you its KClass via ::class, the gateway to this information:
val text = "Hello"
println(text::class) // class kotlin.String
println(text::class.simpleName) // String
class Person(val name: String, val age: Int)
val p = Person("Sara", 25)
println(p::class.simpleName) // PersonAnnotations + reflection is the recipe behind almost every Kotlin/JVM framework:
You add annotations
@Test, @Serializable…
The framework scans
with reflection at startup
It acts on them
runs tests, maps JSON…
Use it sparingly
kotlin-reflect library for full features. Reach for it to build tools and frameworks — not for everyday logic a normal call would handle.Recap & quick check
Key takeaways
- Annotations attach metadata; built-ins include @Deprecated and @JvmStatic (@Override isn't needed).
- Create your own with 'annotation class'; configure with @Target and @Retention.
- @Retention(RUNTIME) is required for an annotation to be visible to reflection.
- Reflection inspects types at runtime via ::class (a KClass).
- Frameworks combine annotations + reflection; use reflection sparingly (slow, unsafe, needs kotlin-reflect).
Quick check
1. What are annotations?
2. What keyword defines a custom annotation?
3. For an annotation to be readable at runtime by reflection, its retention must be…
4. How do you get a value's type information at runtime?
5. How do frameworks like test runners work under the hood?
Great — you've seen the machinery behind Kotlin's frameworks. Next up: Module 28 — Domain-Specific Languages (DSLs).