How can a custom delegate use its `thisRef` and `property: KProperty<*>` parameters? Give a concrete delegate whose behaviour depends on the owner and on the property's name.
answer
- thisRef = owner; call back into it (markDirty, notify)
- Constrain owner type param for typed thisRef access
- property.name → keys, logs, env vars, JSON fields
- property also exposes returnType, annotations, visibility
- Shared delegate must key by property.name
basics
~10 sthisRef is the object that owns the property, so the delegate can read or notify it. property gives the property's name and metadata, useful for keys, logging, or validation messages.
solid answer
~40 s`thisRef` is the owner instance passed into `getValue`/`setValue`. A delegate can use it to call back into the owner (e.g. mark it dirty, fire a change notification on a `PropertyChangeSupport`, or read sibling state). Constrain the owner type parameter to gain typed access — e.g. `ReadWriteProperty<MyEntity, T>` lets `setValue` call `thisRef.markDirty()`. `property: KProperty<*>` exposes `property.name` (most common — used for map keys, log lines, JSON field names, validation messages), plus `returnType`, `annotations`, and visibility. Because the *same* delegate instance can be reused across properties only if it keys by `property.name`, the property parameter is what makes a single delegate object disambiguate which property it's serving. A common pattern: a single delegate object backed by a map keyed on `property.name`, generating a typed accessor per property.
code
kotlin · 15 linesimport kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class Observable<T>(private var v: T, val onChange: (name: String, old: T, new: T) -> Unit) :
ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>) = v
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val old = v; v = value
onChange(property.name, old, value) // uses property.name
}
}
class Model {
var x: Int by Observable(0) { n, o, nw -> println("$n: $o -> $nw") }
}go deeper
Knows thisRef is the owner and property.name is available.
Shows using property.name for keys/logging and that thisRef is the enclosing instance.
Demonstrates owner callbacks via a constrained owner type and disambiguating shared delegates by property.name.
Discusses reflection cost/caching, designing reusable delegate APIs, and notification/change-tracking patterns at scale.
## The two convention parameters Every delegate operator function receives: - `thisRef: R` — the **owner** of the delegated property (the enclosing instance, or `null` for top-level/local properties). - `property: KProperty<*>` — a `kotlin.reflect.KProperty` handle to the property being accessed. These are *passed by the compiler* on every read/write; you don't pass them yourself. ## Using thisRef: callbacks into the owner By constraining the owner type parameter you get typed access to the owner, enabling change tracking or notification: ```kotlin interface Dirtyable { fun markDirty(name: String) } class Tracked<T>(private var value: T) : ReadWriteProperty<Dirtyable, T> { override fun getValue(thisRef: Dirtyable, property: KProperty<*>) = value override fun setValue(thisRef: Dirtyable, property: KProperty<*>, value: T) { if (value != this.value) { this.value = value thisRef.markDirty(property.name) // <- uses BOTH params } } } class Row : Dirtyable { private val changed = mutableSetOf<String>() override fun markDirty(name: String) { changed += name } var title: String by Tracked("") var note: String by Tracked("") } ``` Here `thisRef` is the `Row`, and `property.name` distinguishes `"title"` from `"note"`. ## Using property: name, type, annotations `KProperty<*>` carries reflection metadata: - `property.name` — the source name; used for map keys, log/error messages, serialization field names. - `property.returnType` — the declared `KType`. - `property.annotations` — annotations on the property (read once and cache for performance). - `property.isLateinit`, `visibility`, etc. ```kotlin class Env : ReadOnlyProperty<Any?, String> { override fun getValue(thisRef: Any?, property: KProperty<*>): String = System.getenv(property.name.uppercase()) ?: error("Missing env var for ${property.name}") } // val home: String by Env() -> reads System.getenv("HOME") ``` ## Why both parameters matter for a shared delegate A single delegate *instance* shared across multiple properties must key its state by `property.name`; otherwise all properties would share one slot. That's exactly how map-backed delegation works (`map[property.name]`). `thisRef` similarly lets one delegate serve instances of different owner objects without holding owner state itself. ## Performance note `KProperty.annotations`/`returnType` use reflection and may allocate; if you read them on every access, cache the result keyed by the `KProperty` identity.
- How would you give the delegate typed access to the owner so it can call owner methods?Constrain the owner type parameter, e.g. implement `ReadWriteProperty<MyOwner, T>`; then `thisRef` has type `MyOwner` and you can call its methods directly without casting.
- What's a performance concern when using KProperty reflection in getValue?Reading `property.annotations`/`returnType` triggers reflection and can allocate per call; cache the computed result keyed by the KProperty if getValue is hot.
saying these in an interview costs you the question
- Saying thisRef is the delegate itself (it's the owner of the property)
- Not knowing property.name is the property's source identifier
- Claiming the delegate must store thisRef (it's passed each call)
- Ignoring that one shared delegate needs property.name to disambiguate
- Calling KProperty reflection on every access in a hot path without caching