0Pricing
Kotlin Academy · Lesson

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 short

Object-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 + findAnnotation to drive validation or mapping
  • Cache results per KClass to 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

  1. Kotlin Reflection: KClass, KFunction, KProperty
  2. Creating and Targeting Custom Annotations
  3. Reading Annotations at Runtime
  4. Reflection-Based Validation and Mapping
← Back to Kotlin Academy