Explain precisely how the compiler desugars `var p: T by delegateExpr`. What synthetic members are generated, when is delegateExpr evaluated, and what is the per-access cost?
answer
- synthetic p$delegate field, value has no backing field
- delegate expr evaluated once at init
- getter -> getValue(this, ::p); setter -> setValue
- property ref is cached synthetic KProperty
- no per-access reflection scan
basics
~20 sThe compiler stores the delegate in a hidden field evaluated once at initialization, then makes the property's getter call getValue and its setter call setValue on that field. There is no extra storage for the value itself.
solid answer
~40 s`var p: T by delegateExpr` lowers to: a synthetic field (commonly `p$delegate`) initialized once where the property would be initialized (constructor body for members) by evaluating `delegateExpr`; a generated getter whose body is `return p$delegate.getValue(this, <property ref>)`; and a generated setter whose body is `p$delegate.setValue(this, <property ref>, value)`. The `<property ref>` is a bound/synthetic `KProperty` reference created per declaration. For a top-level property the field is a file-level static and `thisRef` is `null`. The value itself has no backing field — the delegate owns storage. Per-access cost is a single virtual call to getValue/setValue plus passing a cached property reference; no reflection scan happens at runtime. If the optional `provideDelegate` operator is present, an extra `provideDelegate(thisRef, property)` call wraps creation — but that is a sibling topic.
code
kotlin · 10 lines// Source:
class Holder { var p: Int by Box(0) }
// Conceptually generated:
class Holder {
private val `p$delegate` = Box(0) // once
var p: Int
get() = `p$delegate`.getValue(this, ::p)
set(v) { `p$delegate`.setValue(this, ::p, v) }
}go deeper
May only know reads/writes go through a delegate object.
Knows getValue/setValue are called but is fuzzy on the synthetic field and one-time init.
Describes the generated getter/setter, the p$delegate field, once-only init, and per-access cost accurately.
Connects the lowering to performance/ABI reasoning and cleanly separates the provideDelegate boundary.
## The lowering, step by step Given: ```kotlin class Holder { var p: T by delegateExpr } ``` the compiler generates roughly: ```kotlin class Holder { private val p$delegate: D = delegateExpr // evaluated once var p: T get() = p$delegate.getValue(this, ::p) // ::p is a synthetic KProperty ref set(value) { p$delegate.setValue(this, ::p, value) } } ``` Key facts: - **Synthetic delegate field.** The delegate instance lives in a hidden field (`p$delegate`). The property `p` has **no value backing field** — storage is the delegate's responsibility. - **When `delegateExpr` runs.** It is evaluated **once**, at the point the property would normally be initialized: in the constructor body for a member property, in the static initializer for a top-level/companion property, or where a local delegated property is declared. It is **not** re-evaluated per access. - **The property reference.** The compiler emits a `KProperty` reference (a cached synthetic object) and passes it to every getValue/setValue call. - **`thisRef`.** For a member property it is the enclosing instance (`this`); for a top-level or file-level property it is `null`. ## var vs val A `val` generates only the getter (and requires only `getValue`); a `var` generates both accessors. ## Per-access cost Each read is one call to `getValue`; each write one call to `setValue`. These are ordinary (often virtual) method invocations plus handing over a pre-created property reference. **No runtime reflective member scan occurs per access** — the convention is fully resolved at compile time. The only overhead beyond a plain field is the indirection through the delegate object and whatever logic that delegate runs. ## Interaction with provideDelegate (boundary note) If the delegate type defines `operator fun provideDelegate(thisRef, property)`, the compiler inserts `val p$delegate = delegateExpr.provideDelegate(this, ::p)` instead of storing `delegateExpr` directly — a hook to customize/validate the delegate at creation. That operator is its own sibling topic; here the takeaway is only that it changes *what gets stored*, not the getValue/setValue access path. ## Why this matters Because it is a compile-time rewrite, delegation gives reusable access logic with field-like ergonomics and predictable cost, unlike runtime reflective property handling.
- How many times is delegateExpr evaluated for many accesses?Once — at initialization. Subsequent reads/writes reuse the stored delegate instance.
- Is there a value backing field for a delegated property?No. There's a backing field for the delegate instance, not for the property value; the delegate owns storage.
- What changes if the delegate defines provideDelegate?The stored delegate becomes delegateExpr.provideDelegate(thisRef, property)'s result; the getValue/setValue access path is unchanged.
saying these in an interview costs you the question
- Claiming the delegate expression is re-evaluated on every access
- Saying the property keeps its own value backing field in addition to the delegate
- Asserting each access does runtime reflection over the class
- Confusing the delegate field with a value field