Kotlin
2026-08-21 09:00
Every technique so far assumed the compiler knows what you are working with.
Generics stretched that. This unit drops it.
You are writing a JSON serialization library.
You cannot know your users’ classes — they have not been written.
To inspect an arbitrary object at runtime and find its properties.
And a way for the user to say “call this one first_name”
without you knowing anything about their class.
Annotations — metadata attached to a declaration.
Reflection — reading a class’s structure at runtime.
If you have used a test framework or an ORM, you have used both from outside.
@Target and @Retention.callBy.Reflection defers to runtime what the compiler would otherwise check.
That is the whole power and the whole cost.
Kotlin’s @Deprecated carries a ReplaceWith pattern —
so the IDE can offer an automatic fix. Metadata doing real work.
Primitives · strings · enums · class references
other annotations · arrays of these
An ordinary val is not enough — the value is written into the class file.
A backing field. A getter. Sometimes a setter.
Sometimes a constructor parameter.
A Java library expecting the annotation on the field will not find one on the getter.
property · field · get · set · param · setparam · receiver · delegate · file
Most annotation problems with Java libraries are target problems.
When an annotation seems ignored,
check the target first.
Parameters are val constructor properties.
No body — an annotation carries data and nothing else.
Kotlin retains at RUNTIME by default. Which is what reflection needs.
Java does not.
Which is why a Java annotation you went looking for may not be there.
The out is required because KClass<DateSerializer> must be acceptable
where KClass<ValueSerializer<*>> is expected. Unit 9, one page later.
Java reflection — works, but knows nothing of Kotlin concepts.
Kotlin reflection — understands properties, nullability, default values.
KFunction1, KFunction2 — arity in the type, so a typed reference calls directly.
KMutableProperty adds set.
Person::age is the compile-time-safe entry point — unit 5’s member references.
What is new is obtaining these dynamically,
from a class you have never seen.
Kotlin reflection needs a separate dependency, kotlin-reflect.
Not on the classpath by default, because of its size.
A deliberate signal.
A dozen lines.
Working on classes written after it.
@JsonExclude · @JsonName · @CustomSerializer
Reified because that is the only way to write it.
Unit 9’s mechanism where it is genuinely needed.
Reflection without annotations → cannot be customised.
Annotations without reflection → metadata nobody reads.
The library reflects. The user annotates.
Essentially every JVM serialization, persistence and testing framework.
Serialization reads a fully formed object.
Deserialization must create one from a bag of name/value pairs
that may be incomplete, out of order, or wrongly typed.
KCallable.call requires every argument, in order.
JSON guarantees neither.
Order does not matter.
Any parameter left out takes its default value.
An API whose shape only makes sense
once you have hit the problem it solves.
JSON values arrive untyped. Parameters have declared types.
The library consults the parameter’s KType and converts.
Errors a compiler would have caught, now surfacing at runtime.
Nothing is type-checked.
Calls are slower.
Renaming a property breaks code no compiler will flag.
Prefer static structure.
Reach for reflection when you have run out of static structure.
Unit 9’s reified parameters — what makes reflection APIs pleasant.
Unit 9’s star projections — what annotation class parameters need.
Not a coincidence of ordering.
val is not enough.callBy exists because JSON has no order and no obligation to be complete.DSL Construction — the deliberate contrast.
APIs that read like a purpose-built language,
built entirely at compile time.
Kotlin · Annotations and Reflection