Once you have an annotation instance from reflection, how do you read its parameter values, and how do you handle finding annotations that are themselves meta-annotated?
answer
- annotation instance -> read params as properties
- annotationClass gives the annotation's KClass
- meta = check annotationClass.hasAnnotation<T>()
- findAnnotations<T>() (plural) for @Repeatable
- defaults look identical to explicit values
basics
~10 sA reflected annotation is a real object: just read its properties like anno.path. To find an annotation that carries another annotation, look at each annotation's own annotationClass.annotations.
solid answer
~30 sfindAnnotation<T>() returns an instance of T (a synthetic implementation), so you read parameters as ordinary properties: route.path, json.name, etc. Arrays come back as Array<...> (e.g., KClass<*> for class-valued params), enums as enum values, nested annotations as annotation instances. To discover annotations indirectly — e.g., 'find any annotation that is itself meta-annotated with @Qualifier' — you iterate element.annotations and inspect each annotation's annotationClass (a KClass), then check that class's own annotations with hasAnnotation/findAnnotation. There is also findAnnotations<T>() (plural) for @Repeatable annotations, returning a List<T>.
code
kotlin · 11 linesimport kotlin.reflect.full.*
@Retention(AnnotationRetention.RUNTIME) annotation class Qualifier
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class Primary
@Primary class Service
fun main() {
val q = Service::class.annotations.filter { it.annotationClass.hasAnnotation<Qualifier>() }
println(q.map { it.annotationClass.simpleName }) // [Primary]
}go deeper
Reads simple annotation parameters like anno.name as properties.
Handles enums, class (KClass) and array parameters, defaults, and findAnnotations for repeatable.
Implements meta-annotation discovery via annotationClass and explains retention requirements for it.
Designs a stereotype/qualifier resolution model like a DI container, weighing retention, caching, and ambiguity rules.
## Annotations are objects A reflected annotation is a live instance implementing the annotation interface. You read its parameters as properties: ```kotlin @Retention(AnnotationRetention.RUNTIME) annotation class Column(val name: String, val nullable: Boolean = true) val c = field.findAnnotation<Column>()!! println(c.name) // String println(c.nullable) // Boolean (default applied if omitted) ``` Parameter types map predictably: primitives/`String` directly; enums as the enum constant; `KClass<*>` for class references (`val type: KClass<*>`); `Array<...>` for `vararg`/array params; nested annotation types as their instances. ## Defaults If the call site omitted a parameter that has a default, the reflected instance returns that default value — you cannot tell from reflection whether it was explicit or defaulted. ## Meta-annotations (annotations on annotations) Each `Annotation` instance can tell you its declaring annotation class via `annotationClass: KClass<out Annotation>`. That class is itself a `KAnnotatedElement`, so you can inspect *its* annotations: ```kotlin import kotlin.reflect.full.* @Retention(AnnotationRetention.RUNTIME) annotation class Qualifier @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class Primary @Primary class Service val metaQualified = Service::class.annotations.filter { it.annotationClass.hasAnnotation<Qualifier>() } // finds @Primary because Primary is meta-annotated with @Qualifier ``` This is how DI frameworks (Spring, Dagger-style) treat `@Primary`/custom qualifiers as 'stereotypes'. For meta-annotations to be discoverable, the *meta-annotation* (`@Qualifier` here) must also be `RUNTIME`-retained. ## Repeatable annotations Kotlin's `@Repeatable` lets the same annotation appear multiple times. Use the plural helper: ```kotlin val roles: List<Role> = MyClass::class.findAnnotations<Role>() ``` `findAnnotation<T>()` returns the first (or null); `findAnnotations<T>()` returns all instances. ## Practical pattern A common runtime mapper: enumerate `declaredMemberProperties`, for each pull `findAnnotation<Column>()`, fall back to property name when absent, and read `name`/`nullable` to build SQL — all without code generation.
- How do you read all instances of a @Repeatable annotation on a class?Use findAnnotations<T>() (plural), which returns List<T>; findAnnotation<T>() only returns the first.
- How do you get the type of an annotation parameter declared as a class reference?Declare it as KClass<*> in the annotation; reflection returns the KClass instance directly (no .java needed).
saying these in an interview costs you the question
- Thinking you must parse annotation parameters as strings instead of reading typed properties.
- Not knowing annotationClass exists to reach an annotation's own KClass.
- Believing meta-annotations are visible even when the meta-annotation is SOURCE/BINARY.
- Using findAnnotation (singular) for repeatable annotations and missing the rest.
- Assuming reflection can distinguish a defaulted parameter from an explicitly passed equal value.