Why does iterating Kotlin properties via KProperty give a cleaner result than iterating java.lang.reflect Fields, and how do the two views relate?
answer
- Property = field + getter(+setter)
- memberProperties -> KProperty1
- KMutableProperty1 has .set
- javaField null for computed props
- Bridge via kotlin.reflect.jvm
basics
~10 sA Kotlin property is one logical unit, but in bytecode it becomes a field plus getter/setter methods. KProperty shows it as one property; Java reflection shows the separate pieces and may show synthetic ones.
solid answer
~40 sIn Kotlin a `val`/`var` is a property: a (possibly absent) backing field plus a getter and, for `var`, a setter. The JVM has no property concept, so the compiler lowers it to a private field and accessor methods. With java.lang.reflect you must reason across `getDeclaredFields()` and `getDeclaredMethods()` separately, deal with name mangling (e.g. boolean `isX`), and you can be misled by computed properties (no field) or `@JvmField`. kotlin-reflect's `KClass.memberProperties` returns `KProperty1` objects, each unifying name, `returnType` (with nullability), `getter`, and (for `KMutableProperty1`) `setter`. You can read a value with `prop.get(instance)` and bridge down with `prop.javaField` / `prop.javaGetter`. So KProperty is the source-faithful view; Fields/Methods are the lowered JVM view, and the two relate via the jvm bridge extensions.
code
kotlin · 9 linesimport kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField
data class P(val a: Int, val full: String get() = "x$a")
P::class.memberProperties.forEach { p ->
println("${p.name} backingField=${p.javaField != null}")
}
// a backingField=true ; full backingField=falsego deeper
Knows a property compiles to field plus getter/setter and memberProperties lists them.
Explains KProperty1 vs KMutableProperty1, get/set, and computed properties having no field.
Uses the kotlin.reflect.jvm bridge and reasons about @JvmField, mangling, and accessibility/JPMS.
Designs mappers/serializers around the property model, deciding when the metadata cost is justified vs codegen.
## Property vs field In Kotlin, `class P(val a: Int, var b: String?)` declares two **properties**. A property is a source-level concept consisting of: - an optional **backing field** (storage), - a **getter** (always), - a **setter** (only for `var`). The JVM knows only fields and methods, so the compiler **lowers** each property to a private field plus `getA()` / `getB()` / `setB(...)` accessors. ## The Java reflection view (lowered) `clazz.declaredFields` gives `a`, `b` as raw fields (often private), and `clazz.declaredMethods` gives `getA`, `getB`, `setB`. Pain points: - **Computed properties** (`val full get() = ...`) have **no** backing field — they appear only as a method. - **Boolean** properties may be mangled (`isActive` -> getter `isActive`). - `@JvmField` removes accessors and exposes the field directly, changing what you see. - You must manually correlate field + accessors. ## The Kotlin reflection view (source-faithful) ```kotlin import kotlin.reflect.full.memberProperties import kotlin.reflect.KMutableProperty1 data class P(val a: Int, var b: String?) fun main() { val obj = P(1, "x") for (prop in P::class.memberProperties) { val value = prop.get(obj) // unified read val mutable = prop is KMutableProperty1<*, *> println("${prop.name}=${value} nullable=${prop.returnType.isMarkedNullable} mutable=$mutable") } } ``` - `memberProperties` returns `KProperty1<T, R>` — `1` means it takes one receiver (the instance). - `KMutableProperty1` adds `.set(instance, value)`. - Each property carries `returnType` (with `isMarkedNullable`), `visibility`, `getter`, and `setter`. ## Relating the two views (bridge) From `kotlin.reflect.jvm`: - `prop.javaField` -> the backing `Field?` (null for computed properties), - `prop.javaGetter` -> the getter `Method?`, - `(prop as KMutableProperty1).javaSetter` -> the setter `Method?`. And `field.kotlinProperty` goes the other direction. ## Why it matters in practice Serialization, ORMs, and DTO mappers prefer `memberProperties` because they get one clean handle per logical field, with nullability and mutability, instead of guessing across fields and mangled accessor names. ## Accessibility Reading a private property reflectively may need `prop.isAccessible = true` (from `kotlin.reflect.jvm`), which calls through to the Java `setAccessible` and is subject to JPMS module rules.
- Why might prop.javaField be null?For computed properties (a custom getter with no stored value) there is no backing field, so javaField returns null.
- How do you reflectively change a var property's value?Cast to KMutableProperty1 and call set(instance, newValue), setting isAccessible = true first if it is private.
Java reflection hands you the disassembled parts (screws, panels); KProperty hands you the assembled drawer with a label.
saying these in an interview costs you the question
- Treating Kotlin properties and Java fields as one-to-one always
- Forgetting computed properties have no backing field
- Not handling boolean accessor mangling when using Java reflection
- Ignoring isAccessible for private members