Show how the compiler desugars `val x: T by expr` when `expr` has a provideDelegate operator, including the hidden field and the KProperty argument.
answer
- x$delegate field + getValue(this, ::x)
- with provideDelegate: field = expr.provideDelegate(this, ::x)
- convention checked on RETURN type, not expr type
- thisRef null for top-level/local
- property.name is cheap, no kotlin-reflect
basics
~20 sKotlin creates a hidden field for the delegate. If provideDelegate exists, it first calls expr.provideDelegate(owner, property) and stores that result in the field. Then every read calls getValue on that stored field, passing the owner and the property metadata.
solid answer
~40 sWithout provideDelegate, `val x: T by expr` desugars to a hidden field `x$delegate = expr` plus a getter that returns `x$delegate.getValue(this, ::x)`. When `expr`'s type declares `operator fun provideDelegate`, the compiler inserts an extra step: `x$delegate = expr.provideDelegate(this, this::x)`. The `this::x` (a `KProperty<*>` reference, materialized via reflection metadata) is passed to provideDelegate at creation and to getValue/setValue on each access. So provideDelegate's return type — not expr's type — must satisfy the getValue/setValue convention. For top-level or local properties, the owner argument is null. The KProperty is a synthetic property reference the compiler emits; accessing property.name does not require kotlin-reflect at runtime because the name is baked into metadata.
code
kotlin · 11 linesclass Factory(val v: Int) {
operator fun provideDelegate(thisRef: Any?, p: KProperty<*>): Delegate =
Delegate(v, p.name)
}
class Delegate(val v: Int, val name: String) {
operator fun getValue(thisRef: Any?, p: KProperty<*>): Int = v
}
class Host { val n: Int by Factory(42) }
// generated: n$delegate = Factory(42).provideDelegate(this, this::n)
// get() = n$delegate.getValue(this, this::n)go deeper
Recognizes there is a hidden field and getValue call, even if hazy on provideDelegate specifics.
Can write both desugared forms and state that the return type must satisfy the convention.
Explains owner values per property kind and the factory-vs-delegate separation the desugaring enables.
Reasons about reflection cost, metadata footprint, and how this shapes a typed DSL/ORM column API.
## The two desugaring forms Kotlin compiles `by` into a **hidden delegate field** plus accessors that follow the *operator convention* (`getValue`/`setValue`). Whether `provideDelegate` participates depends solely on whether the right-hand expression's type declares it. ### Without provideDelegate ```kotlin class C { val x: String by expr } ``` desugars (conceptually) to: ```kotlin class C { private val x$delegate = expr val x: String get() = x$delegate.getValue(this, this::x) } ``` - `x$delegate` — a synthetic field holding the delegate. - `this::x` — a `KProperty<*>` **property reference** the compiler creates; it carries the property's `name` and annotations. - For a `var`, the setter calls `x$delegate.setValue(this, this::x, value)`. ### With provideDelegate If `typeof(expr)` declares `operator fun provideDelegate(thisRef, KProperty<*>)`, the field initializer changes: ```kotlin class C { private val x$delegate = expr.provideDelegate(this, this::x) val x: String get() = x$delegate.getValue(this, this::x) } ``` **Key consequence:** the type stored in `x$delegate` is now `provideDelegate`'s *return type*, and **that** type — not `expr`'s — must satisfy `getValue`/`setValue`. `expr` itself only needs `provideDelegate`. ## Owner (thisRef) values - **Member property:** `thisRef` is the enclosing instance (`this`). - **Top-level property:** `thisRef` is `null` (type `Nothing?`). - **Local property:** `thisRef` is `null` as well; the field is a local val. ## The KProperty argument and reflection The `this::x` reference is a **bound/unbound property reference** the compiler emits using lightweight reflection metadata. Reading `property.name` is cheap and does **not** pull in the full `kotlin-reflect` dependency. Heavy reflection (e.g., resolving the property's getter via `kotlin-reflect`) would. ## Why the distinction matters Because the convention is checked on the **return type** of `provideDelegate`, you can have a *factory* object (the `expr`) that is completely different from the *delegate* object (its return value). That separation is what enables validation/selection at creation time while keeping a clean per-access delegate. ```kotlin class Column<T>(val type: String) // factory, only provideDelegate class BoundColumn<T>(val name: String, val type: String) // delegate, has getValue operator fun <T> Column<T>.provideDelegate( thisRef: Any?, prop: KProperty<*> ): BoundColumn<T> = BoundColumn(prop.name, type) operator fun <T> BoundColumn<T>.getValue(thisRef: Any?, prop: KProperty<*>): T = TODO("read column $name of type $type") ```
- Which type must satisfy getValue when provideDelegate is present — the expr type or the return type?The return type of provideDelegate. expr only needs provideDelegate itself.
- Is the property reference passed to getValue the same metadata used by provideDelegate?Yes, both receive a KProperty<*> for the same property; only the call site (creation vs access) differs.
saying these in an interview costs you the question
- Claiming expr's type must define getValue even when provideDelegate is used
- Forgetting the hidden $delegate field
- Saying property.name requires kotlin-reflect
- Thinking thisRef is the enclosing instance for top-level properties