How do the `getValue`/`setValue`/`provideDelegate` operator conventions support `by`-delegated properties, and how is this different from the `get`/`set` indexing operators?
answer
- by → getValue/setValue(thisRef, KProperty, [value])
- provideDelegate runs at construction, can validate/swap
- ReadOnlyProperty/ReadWriteProperty, lazy, observable, map delegation
- get/set back [] indexing, no KProperty
- both use operator modifier, different syntax
basics
~20 sProperty delegation (val x by delegate) uses getValue and setValue operator functions on the delegate, with an optional provideDelegate to customize creation. The get/set operators are different — they back the [] indexing syntax, not the by keyword.
solid answer
~40 s`val/var name by delegate` desugars to calls on the delegate's `operator fun getValue(thisRef, property)` and, for `var`, `operator fun setValue(thisRef, property, value)`, where `property` is a `KProperty<*>` reflecting the delegated property. An optional `operator fun provideDelegate(thisRef, property)` runs at construction to return (and possibly validate/replace) the actual delegate. The stdlib provides `ReadOnlyProperty`/`ReadWriteProperty` interfaces and helpers like `lazy`, `Delegates.observable`, and map delegation. This is distinct from the indexing operators `get`/`set`, which power `a[i]`/`a[i] = v` in builder/collection DSLs. Both use the `operator` modifier and are convention-based, but `by` targets *property* access (KProperty-aware, one value per property) while `[]` targets *subscript* access (key/index-addressed). Delegation is a DSL/config idiom (e.g. config `by` map, settings `by` env).
code
kotlin · 19 linesimport kotlin.reflect.KProperty
class Audited<T>(private var value: T) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
println("${property.name}: $value -> $newValue")
value = newValue
}
operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): Audited<T> {
require(property.name.isNotBlank())
return this
}
}
class Config { var port: Int by Audited(8080) }
// vs indexing:
class Bag { val m = mutableMapOf<String, Int>()
operator fun get(k: String) = m[k]
operator fun set(k: String, v: Int) { m[k] = v } }go deeper
Recognizes by lazy exists but likely can't name the convention functions.
Knows getValue/setValue back delegation and can use stdlib delegates.
Explains the thisRef/KProperty signatures and contrasts with [] indexing operators.
Adds provideDelegate construction-time validation, frames delegation as a composition mechanism, and weighs its indirection/perf trade-offs in a DSL/config design.
## Delegated properties and the `by` keyword Writing `val x: T by delegateExpr` tells the compiler to route reads (and writes for `var`) of `x` through the **delegate object**, using operator-convention functions: ```kotlin class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "value of ${property.name}" operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("set ${property.name} = $value") } } class Holder { var field: String by Delegate() } ``` - Reading `holder.field` → `delegate.getValue(holder, ::field)`. - Assigning `holder.field = "x"` → `delegate.setValue(holder, ::field, "x")`. - `thisRef` is the owner (or `null` for top-level), `property` is a **`KProperty<*>`** giving the property's name/metadata via reflection. The stdlib offers `ReadOnlyProperty<R, T>` and `ReadWriteProperty<R, T>` functional interfaces so you can implement delegates as lambdas, plus ready-made delegates: `lazy { }`, `Delegates.observable`, `Delegates.vetoable`, `Delegates.notNull()`, and **map delegation** (`val name: String by map`). ## `provideDelegate` If the delegate type (or an extension on it) defines: ```kotlin operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): ReadWriteProperty<Any?, T> ``` then at **construction time** the compiler calls `provideDelegate` instead of using the expression directly. This lets you validate the property (e.g. check an annotation or name) or pick a concrete delegate up front — used by frameworks to register/observe properties when the owner is created. ## How this differs from `get`/`set` indexing | Aspect | `getValue`/`setValue` (`by`) | `get`/`set` (`[]`) | |---|---|---| | Trigger syntax | `val/var x by d` | `a[i]`, `a[i] = v` | | Extra parameters | `thisRef`, `KProperty<*>` | just indices/keys | | Granularity | one value per *property* | many keyed/indexed slots | | Reflection-aware | yes (`KProperty`) | no | | Typical use | lazy/observable/config-binding DSLs | collection/builder subscript DSLs | Both are **operator conventions** marked with `operator`, but they back entirely different syntactic forms. A config DSL might use *both*: `val host: String by settings` (delegation) and `settings["host"] = "x"` (indexing). ## Why principals care - Delegation moves cross-cutting behavior (laziness, change tracking, binding) out of the property and into reusable delegates — a composition tool, not just sugar. - `provideDelegate` enables construction-time validation, catching misconfiguration early. - Overusing delegation hides control flow and adds an indirection that complicates debugging and performance reasoning (each access is a virtual call; `lazy` adds synchronization unless `LazyThreadSafetyMode.NONE`).
- What is the `property: KProperty<*>` parameter used for?It reflects the delegated property — most commonly `property.name` (e.g. for map-key lookup or logging), but also annotations and metadata. Indexing `get`/`set` get no such reflection.
- When does `provideDelegate` run, and why is that useful?At owner construction (when the delegate is created), letting you validate or substitute the delegate early — e.g. assert a required env var exists before any access.
A delegate is a property's outsourced manager (it knows the property's name and owner); indexing operators are more like a mailbox wall addressed by slot number.
saying these in an interview costs you the question
- Conflating `getValue`/`setValue` with the `get`/`set` indexing operators
- Forgetting the `KProperty<*>` parameter or `thisRef`
- Thinking `by` works without `operator`-marked convention functions
- Claiming `provideDelegate` runs on every access (it runs once, at construction)
- Ignoring performance/indirection costs of heavy delegation