skip to content

When you write `class W(impl: Foo) : Foo by impl`, is the `impl` value stored and accessible inside the class? How do you access the delegate if you need it?

level: middleimportance: should knowfreq 35%

answer

  1. Bare param ≠ property → can't reference later
  2. Add `val` to access the delegate
  3. `by` expression evaluated once at construction
  4. Reassigning a `var` delegate does NOT redirect forwarders
  5. Delegate expr can be any expression, not just a param

basics

~10 s

The compiler stores the delegate in a hidden field, but a plain constructor parameter is not a property, so you can't reference it later. To use it inside the class, declare it as val.

solid answer

~40 s

The expression after `by` is evaluated once and stored in a synthetic field the compiler uses for forwarding. But that field is not directly visible to your code. If you write `class W(impl: Foo) : Foo by impl`, `impl` is a constructor parameter scoped to the constructor — it is NOT a property, so you cannot reference `impl` in methods. To both delegate to it and access it (e.g., in an override that wants to call the delegate), declare it as a property: `class W(private val impl: Foo) : Foo by impl`. Then `impl` is a real field you can call. Note: the `by`-forwarders use the captured value at construction; reassigning a `var` delegate does NOT redirect already-generated forwarders, because they bind to the initial value.

go deeper

for a junior

Knows you must add val to reference the delegate later.

for a middle

Distinguishes parameter vs property scope and knows forwarders capture the value at construction.

for a senior

Explains the var-reassignment non-redirection and that the by expression is any once-evaluated expression.

for a principal

Considers API ergonomics of exposing vs hiding the delegate and immutability guarantees this gives wrappers.

## Constructor parameter vs property In Kotlin, a constructor parameter written without `val`/`var` is **only** in scope during initialization — it is not a class member: ```kotlin class W(impl: Foo) : Foo by impl { fun touch() = impl.bar() // ERROR: 'impl' is not accessible here } ``` The delegate forwarding still works (the compiler captured the value into a synthetic field), but **you** cannot name `impl` outside the constructor. ## Making the delegate accessible Add `val` (or `private val`) so it becomes a real property: ```kotlin class W(private val impl: Foo) : Foo by impl { override fun bar() { log("before") impl.bar() // now legal } } ``` This is the common idiom when you override a member and still want to call through to the delegate (the Decorator pattern). ## Two fields, subtle point When you write `private val impl` AND `by impl`, conceptually there is the property `impl` plus the delegation. In current Kotlin the compiler is smart enough that the `by impl` forwarders read from your `impl` property field — they don't create a second independent copy that diverges. However, the value used for forwarding is the one bound when the object is constructed. ## `var` delegates and reassignment ```kotlin class W(var impl: Foo) : Foo by impl ``` The generated forwarders bind to the **value at construction time**. Reassigning `impl` later updates the property but does **not** change where the forwarders point. So delegation is effectively fixed at construction — do not expect mutable redirection. ## The `by` expression can be any expression It need not be a constructor parameter: ```kotlin class W : Foo by DefaultFoo() // fresh instance class X(list: List<Int>) : List<Int> by list.toList() // a transformed copy ``` The expression is evaluated once during construction. ## Summary - Bare parameter → forwarding works, but name is unavailable later. - `val`/`private val` parameter → forwarding works AND you can reference it. - Forwarders capture the value at construction; mutating a `var` does not redirect them.

  • Why declare the delegate `private val` in a Decorator?
    So an overridden member can still call through to the delegate's original behavior while keeping the delegate hidden from the public API.
  • If you make the delegate a `var` and reassign it, do calls suddenly go to the new object?
    No. The generated forwarders bind to the value captured at construction; reassignment does not redirect them.

saying these in an interview costs you the question

  • Thinking a bare constructor parameter is accessible in methods
  • Believing reassigning a `var` delegate redirects the forwarders
  • Assuming the `by` expression is re-evaluated on each call
  • Not knowing `by` accepts any expression, not just a parameter
  • Claiming you must duplicate the field manually to access the delegate

context