Reflection-Based Validation and Mapping
Build a simple validator or object mapper using annotation-driven reflection.
Reflection-Based Validation and Mapping is a free Kotlin Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What This Pattern Solves
Reflection-based validation reads annotations from a class's properties at runtime and applies validation rules automatically — no manual checks per field. The same technique powers ORM mappers that convert between objects and database rows.
The Validate Annotation
Define a runtime-retained annotation targeting properties:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY)
annotation class Validate(
val minLength: Int = 0,
val maxLength: Int = Int.MAX_VALUE,
val pattern: String = ""
)Annotating a Data Class
Apply @Validate to data class properties. The validator will scan these at runtime:
data class UserForm(
@Validate(minLength = 2, maxLength = 50) val name: String,
@Validate(pattern = "^[\\w.]+@[\\w]+\\.[a-z]{2,}$") val email: String,
@Validate(minLength = 8) val password: String
)The Validator Function
Use KClass.declaredMemberProperties to iterate properties, find annotations, and collect errors:
fun validate(obj: Any): List<String> {
val errors = mutableListOf<String>()
for (prop in obj::class.declaredMemberProperties) {
val ann = prop.findAnnotation<Validate>() ?: continue
val value = (prop as KProperty1<Any, *>).get(obj)?.toString() ?: ""
if (value.length < ann.minLength) errors += "${prop.name}: too short"
if (value.length > ann.maxLength) errors += "${prop.name}: too long"
if (ann.pattern.isNotEmpty() && !value.matches(Regex(ann.pattern)))
errors += "${prop.name}: invalid format"
}
return errors
}Running the Validator
Call validate() with any annotated object. The function returns a list of error messages; an empty list means valid:
val form = UserForm("A", "not-an-email", "short")
val errors = validate(form)
errors.forEach(::println)
// name: too short
// email: invalid format
// password: too shortObject-to-Map Mapping with @Column
Another reflection pattern maps an object to a Map using a @Column annotation to provide column names:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY)
annotation class Column(val name: String)
fun toMap(obj: Any): Map<String, Any?> =
obj::class.declaredMemberProperties
.mapNotNull { prop ->
val col = prop.findAnnotation<Column>() ?: return@mapNotNull null
col.name to (prop as KProperty1<Any, *>).get(obj)
}.toMap()Using the Mapper
Annotate the data class and call toMap() to get a column-keyed map ready for an INSERT statement:
data class User(
@Column("user_name") val name: String,
@Column("user_email") val email: String
)
val user = User("Alice", "alice@example.com")
println(toMap(user))
// {user_name=Alice, user_email=alice@example.com}Caching Reflection Results
Reflection scans are expensive. Cache the property-to-annotation mapping at startup using a ConcurrentHashMap keyed by KClass:
val cache = ConcurrentHashMap<KClass<*>, List<Pair<KProperty1<Any,*>, Column>>>()
fun columnsOf(klass: KClass<*>) = cache.getOrPut(klass) {
klass.declaredMemberProperties
.mapNotNull { p ->
p.findAnnotation<Column>()?.let { (p as KProperty1<Any,*>) to it }
}
}Combining Validation and Mapping
You can stack annotations on a single property and process each independently. First validate, then map — or do both in a single pass by collecting each annotation type:
data class Product(
@Validate(minLength = 1) @Column("product_name") val name: String,
@Column("price") val price: Double
)Limitations
Reflection-based validation is powerful but has trade-offs: it adds runtime overhead, it relies on annotation retention, and it cannot catch errors at compile time. For performance-critical or compile-time-safe validation, consider KSP-generated code instead.
When to Use This Pattern
Reflection-based validation shines in framework code that must handle arbitrary user-defined classes: ORMs, serializers, form validators, configuration binders. Application code that owns its types should prefer explicit validation for speed and clarity.
Quick Check
Which annotation retention policy is required for a validation annotation to be readable at runtime?
Recap: Reflection-Based Validation and Mapping
Key takeaways:
- Declare runtime-retained annotations targeting properties
- Use
declaredMemberProperties+findAnnotationto drive validation or mapping - Cache results per
KClassto avoid repeated reflection overhead - Stack multiple annotations on a property for combined behavior
- Consider KSP for compile-time alternatives in performance-critical paths
Frequently asked questions
Is the “Reflection-Based Validation and Mapping” lesson free?
Yes — the full text of “Reflection-Based Validation and Mapping” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.
What will I learn in “Reflection-Based Validation and Mapping”?
Build a simple validator or object mapper using annotation-driven reflection. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Kotlin Academy?
No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Reflection-Based Validation and Mapping” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Kotlin Academy lesson?
Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Kotlin Reflection: KClass, KFunction, KProperty
- Creating and Targeting Custom Annotations
- Reading Annotations at Runtime
- Reflection-Based Validation and Mapping