skip to content

Can you use `by` delegation on local variables and top-level properties? What does the compiler pass as thisRef in those cases, and what constraints does that place on the delegate's signatures?

level: seniorimportance: nice to knowfreq 25%

answer

  1. delegation works on members, top-level, and locals
  2. top-level/local -> thisRef is null
  3. delegate must accept nullable owner (Any?)
  4. non-null thisRef restricts to that owner type
  5. stdlib lazy/Map use Any? for reuse

basics

~20 s

Yes — delegation works on top-level properties and local variables, not just class members. In those cases there is no owning object, so the delegate receives null as the owner argument, which means the delegate must accept a nullable owner type.

solid answer

~40 s

Delegation is supported on member properties, top-level properties, and local `val`/`var` variables. For members, `thisRef` is the enclosing instance. For top-level and local delegated properties there is no owner, so the compiler passes `null` for `thisRef`. Therefore the delegate's `getValue`/`setValue` must declare a `thisRef` type that accepts `null` — typically `Any?` — or the code won't compile for those uses. A local delegated property still desugars to getValue/setValue calls; the property reference is generated just as for members. This is why general-purpose delegates (and the stdlib `Map` and `lazy` delegates) type `thisRef` as `Any?`. If a delegate types `thisRef` as a specific non-null class, it can only be used by member properties of compatible owners.

code

kotlin · 5 lines
kotlin
class OwnerOnly {
    operator fun getValue(thisRef: Any, property: KProperty<*>) = 1 // non-null!
}
// val x by OwnerOnly()      // ERROR: top-level owner is null
class C { val ok by OwnerOnly() } // OK: thisRef is the C instance

go deeper

for a junior

Likely only aware of member-property delegation.

for a middle

Knows top-level and local delegation exist but may miss the null thisRef consequence.

for a senior

Explains that thisRef is null for top-level/local and the resulting Any? signature requirement.

for a principal

Uses thisRef typing deliberately as an API constraint to scope a delegate to particular owners.

## Where `by` is allowed Property delegation isn't limited to class members. You can write: - **Member property:** `class C { val a by d }` - **Top-level property:** `val b by d` at file scope - **Local variable:** inside a function, `val c by d` (since the delegate convention applies to locals too) ## What `thisRef` is in each case `thisRef` is the *owner* of the property: - For a **member** property, it is the enclosing instance (`this`). - For a **top-level** property and a **local** variable, there is no owning object, so the compiler passes **`null`**. ## Constraint on the delegate signature Because `null` may be passed, the delegate's functions must declare a `thisRef` parameter type that **accepts null**: ```kotlin operator fun getValue(thisRef: Any?, property: KProperty<*>): T ``` Use `Any?` (or another nullable supertype). If instead you write a non-null specific type: ```kotlin operator fun getValue(thisRef: SomeClass, property: KProperty<*>): T ``` then that delegate can only back **member properties of `SomeClass`** (or subtypes) — it cannot back a top-level or local property, and the compiler rejects such usage. This is exactly why reusable stdlib delegates (`lazy`, `Map`) type `thisRef` as `Any?`. ## Local example ```kotlin fun demo() { val token: String by lazy { computeToken() } if (cond) println(token) // delegate computed lazily; thisRef is null } ``` Here `token` is a local delegated property; access desugars to `getValue(null, ::token)`. ## Practical takeaway Widen `thisRef` to `Any?` for general delegates; narrow it deliberately only when you *want* to restrict the delegate to a specific owner type as an API constraint.

  • Why do stdlib delegates type thisRef as Any?
    So one delegate works for members, top-level, and local properties, including the null-owner cases.
  • What happens if a delegate types thisRef as a non-null specific class?
    It can only back member properties of that class or subtypes; top-level/local usage won't compile.

saying these in an interview costs you the question

  • Believing delegation only works on class members
  • Typing thisRef non-null and expecting it to work for top-level properties
  • Thinking top-level delegates pass the file or KClass as thisRef
  • Assuming locals can't be delegated

context