You need to build a generic 'object to map' serializer using declaredMemberProperties. How do you read each property's value and type generically, and what pitfalls (visibility, isAccessible, nullability) must you handle?
answer
- declaredMemberProperties → KProperty1; get(instance) reads it
- private getter → IllegalCallableAccessException without isAccessible=true
- isAccessible is in kotlin.reflect.jvm
- filter on visibility to skip non-public
- cache property lists; reflection is slow
basics
~10 sLoop over the class's declaredMemberProperties, call get(instance) on each to read the value, and use property.returnType for the type. Watch out for private properties (set isAccessible) and nullable values.
solid answer
~40 sUse `kClass.declaredMemberProperties`, which yields `KProperty1<T, *>`. For each, `prop.get(instance)` reads the value via its single receiver, and `prop.returnType: KType` gives the declared type (use `returnType.isMarkedNullable` and `returnType.classifier` to inspect it). Pitfalls: (1) **Visibility** — `private`/`protected` getters throw `IllegalCallableAccessException` unless you set `prop.isAccessible = true` (from `kotlin.reflect.jvm`), which bypasses JVM access checks. (2) **Filtering** — `declaredMemberProperties` excludes inherited and extension properties; use `memberProperties` if you want inherited ones, and filter on `visibility` to skip non-public. (3) **Nullability/typing** — values come back as `Any?`; respect `isMarkedNullable`. (4) **Cost** — reflection is far slower than direct access; cache the property list. Mutable writes need a `KMutableProperty1` and `set(instance, value)`.
code
kotlin · 10 linesimport kotlin.reflect.KProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.isAccessible
fun describe(obj: Any) = obj::class.declaredMemberProperties.map {
@Suppress("UNCHECKED_CAST")
val p = it as KProperty1<Any, *>
p.isAccessible = true
Triple(p.name, p.returnType, p.get(obj))
}go deeper
Can loop properties and call get(instance) but may miss visibility and nullability concerns.
Handles isAccessible and visibility filtering and reads returnType correctly.
Inspects KType (classifier, nullability, arguments), caches property lists, and handles mutable writes safely.
Weighs reflection cost vs. codegen/serialization plugins and designs a cached, visibility- and nullability-correct generic mapper.
## The core loop `KClass.declaredMemberProperties` returns `Collection<KProperty1<T, *>>` — the class's own properties, each readable with **one receiver** (the instance): ```kotlin import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.jvm.isAccessible fun toMap(obj: Any): Map<String, Any?> = obj::class.declaredMemberProperties.associate { prop -> @Suppress("UNCHECKED_CAST") val p = prop as kotlin.reflect.KProperty1<Any, *> p.isAccessible = true // bypass JVM visibility checks prop.name to p.get(obj) // read via the single receiver } data class User(val id: Long, val name: String, private val secret: String = "x") println(toMap(User(1, "Ada"))) // {id=1, name=Ada, secret=x} ``` Note `obj::class` gives the **runtime** class. `prop.get(obj)` invokes the getter; `prop.returnType` is the declared `KType`. ## Pitfall 1 — Visibility and `isAccessible` Reading a `private`/`protected` property reflectively throws **`IllegalCallableAccessException`**. Setting `prop.isAccessible = true` (extension in `kotlin.reflect.jvm`) suppresses the JVM access check. If you instead want to **skip** non-public properties, filter on `KCallable.visibility`: ```kotlin val publicProps = obj::class.declaredMemberProperties .filter { it.visibility == kotlin.reflect.KVisibility.PUBLIC } ``` ## Pitfall 2 — What's included `declaredMemberProperties` **excludes** inherited and extension properties. For inherited fields use `memberProperties`. Neither returns Java fields directly — they are Kotlin properties. ## Pitfall 3 — Types and nullability Each read returns `Any?`. Inspect the declared type via `KType`: - `prop.returnType.isMarkedNullable` — was it declared nullable? - `prop.returnType.classifier` — the `KClassifier` (often a `KClass`). - `prop.returnType.arguments` — generic type arguments. ## Pitfall 4 — Performance and caching Reflection (`get`) is **substantially slower** than direct field access and allocates. For hot paths, **cache** the `List<KProperty1<…>>` per class rather than re-enumerating, and consider `isAccessible = true` once. The whole API requires the `kotlin-reflect` artifact on the runtime classpath. ## Writing back To set values, narrow to `KMutableProperty1` and call `set(instance, value)`; `val`s have no setter.
- What exception is thrown when you reflectively read a private property without enabling access?IllegalCallableAccessException; set isAccessible = true (from kotlin.reflect.jvm) to allow it.
- How do you determine whether a property value can legitimately be null?Check prop.returnType.isMarkedNullable on the KType.
- Why cache the declaredMemberProperties result?Enumerating members and reflective get calls are slow and allocate; caching the list avoids repeating that work per object.
saying these in an interview costs you the question
- Forgetting private properties need isAccessible = true
- Assuming declaredMemberProperties includes inherited/extension properties
- Ignoring nullability and casting blindly to non-null types
- Re-enumerating properties on every call in a hot path