skip to content

Explain the difference between KProperty0, KProperty1, and KProperty2, and where KMutableProperty fits in.

level: middleimportance: should knowfreq 40%

answer

  1. 0/1/2 = number of receivers to read
  2. KProperty1 = MyClass::prop and declaredMemberProperties
  3. KProperty0 = bound/top-level (no receiver)
  4. KMutableProperty adds setter for var
  5. getter/setter are themselves KFunctions

basics

~10 s

The number means how many receivers you must pass to read the property. KProperty0 needs none, KProperty1 needs one (the object), KProperty2 needs two. KMutableProperty versions also let you write the value.

solid answer

~30 s

`KProperty<V>` models a property; its subtypes differ by **receiver count** required by the getter. `KProperty0<V>` (e.g. a top-level or bound property) needs zero receivers — `get()`. `KProperty1<T, V>` needs one receiver of type T — `get(receiver: T)`; this is what you get from `SomeClass::prop` and from `declaredMemberProperties`. `KProperty2<D, E, V>` needs two (dispatch + extension receivers), typically a member extension property. Each has a `getter` (itself a `KProperty.Getter`, a `KFunction`). The **`KMutableProperty0/1/2`** variants add a `setter` and a corresponding `set(...)` to write `var` values. `declaredMemberProperties` returns `KProperty1` (or `KMutableProperty1` for `var`s), since you supply the instance to read each one.

code

kotlin · 9 lines
kotlin
import kotlin.reflect.KMutableProperty0

class Profile(var nickname: String)

val p = Profile("neo")
val bound: KMutableProperty0<String> = p::nickname  // 0 receivers, captured p
println(bound.get())   // neo
bound.set("trinity")
println(p.nickname)    // trinity

go deeper

for a junior

Understands the digit indicates how many receivers and that mutable variants exist for var.

for a middle

Correctly maps bound vs unbound references to KProperty0 vs KProperty1 and reads/writes via get/set.

for a senior

Explains KProperty2 member extension properties and that getter/setter are KFunctions with their own metadata.

for a principal

Reasons about API design choices behind arity-typed properties and how frameworks dispatch generically over them.

## Receivers, briefly A **receiver** is the object on which a property is accessed. `user.name` has `user` as the (dispatch) receiver. The `KProperty` subtypes are named by **how many receivers** you must supply to read the value reflectively. ## The three arities - **`KProperty0<out V>`** — **zero** receivers. Read with `get()`. You get this from a **bound** reference (`instance::prop`), a top-level property reference (`::topLevelVal`), or a local. `value` returns `V` directly. - **`KProperty1<T, out V>`** — **one** receiver of type `T`. Read with `get(receiver: T)`. This is the type returned by an **unbound** member reference `MyClass::prop` and by `KClass.declaredMemberProperties`. - **`KProperty2<D, E, out V>`** — **two** receivers: a dispatch receiver `D` and an extension receiver `E`. Read with `get(d, e)`. Produced by **member extension properties**. ## Mutable variants For each arity there is a **`KMutableProperty0/1/2`** that the property exposes when it is a `var`. They add a **`setter`** and a `set(...)` overload to write: ```kotlin import kotlin.reflect.KMutableProperty1 import kotlin.reflect.full.declaredMemberProperties class Counter(var value: Int, val label: String) val c = Counter(0, "hits") val props = Counter::class.declaredMemberProperties // KProperty1<Counter, *> // Read with one receiver: val label = props.first { it.name == "label" }.get(c) // "hits" // Write only if it's a var → KMutableProperty1 val valueProp = props.first { it.name == "value" } if (valueProp is KMutableProperty1<*, *>) { @Suppress("UNCHECKED_CAST") (valueProp as KMutableProperty1<Counter, Int>).set(c, 42) } println(c.value) // 42 ``` ## Bound vs unbound - `Counter::value` is **unbound** → `KMutableProperty1<Counter, Int>` (you pass the receiver each call). - `c::value` is **bound** → `KMutableProperty0<Int>` (receiver captured; `get()`/`set(v)` take no instance). ## Getters/setters are callables too `prop.getter` is a `KProperty.Getter` and `prop.setter` is a `KMutableProperty.Setter`, both of which are themselves `KFunction`s — so they have their own annotations, visibility, and `call`.

  • Which KProperty subtype does declaredMemberProperties return and why?
    KProperty1 (KMutableProperty1 for vars) because you must supply the instance as the single receiver to read each property.
  • How do you know at runtime whether you can write to a property?
    Check if it is a KMutableProperty (e.g. `is KMutableProperty1<*, *>`); only then is there a setter.

saying these in an interview costs you the question

  • Saying the digit means parameter count of the property rather than receiver count
  • Assuming every property has a setter regardless of val/var
  • Confusing bound (KProperty0) with unbound (KProperty1) references
  • Not knowing declaredMemberProperties yields KProperty1

context