Phase 4 · Collections & GenericsModule 23~34 min read

Delegation & Delegated Properties

Reuse behavior with class delegation and powerful delegated properties like lazy.

What you'll learn

Delegation is "handing off work to someone else," and Kotlin bakes it right into the language with the by keyword. It powers two great features: class delegation (implement an interface by forwarding to another object) and delegated properties (like the famous lazy).

By the end you'll be able to:

  • Implement an interface via class delegation with by
  • Use delegated properties
  • Apply lazy for one-time initialisation
  • React to changes with observable

Class delegation

Say you want a class that implements an interface but forwards most of the work to another object (the classic decorator pattern). In Java you'd write a forwarding method for every interface method. Kotlin does it in one word — by:

Delegating an interface

Your class

Logger

by

A delegate

ConsolePrinter

=

Result

calls forwarded, zero boilerplate

Delegation.kt
interface Printer {
    fun print(msg: String)
}

class ConsolePrinter : Printer {
    override fun print(msg: String) = println("Printing: $msg")
}

// 'by' delegates the whole Printer interface to the given object
class Logger(printer: Printer) : Printer by printer

Logger(ConsolePrinter()).print("Hello")   // forwarded automatically

Tip

class Logger(printer: Printer) : Printer by printer makes Logger implement Printer by forwarding every call to printer — and you can still override any single method to customise it. It's composition made effortless.

Delegated properties

The by keyword also works on properties: instead of storing a value directly, a property can delegate its get/set logic to a helper object. Kotlin's standard library ships several ready-made delegates — the most famous being lazy.

lazy

by lazy { ... } computes a value the first time the property is read, then caches it for every subsequent access. It's thread-safe by default — perfect for expensive objects you don't want to create until (and unless) they're actually needed:

Lazy.kt
val heavy: String by lazy {
    println("Computing...")     // runs ONCE, only on first access
    "expensive result"
}

println("Before access")
println(heavy)     // triggers the computation now
println(heavy)     // cached — no recomputation

observable

Delegates.observable runs a callback every time the property changes, giving you the old and new values — great for reacting to state changes (updating a UI, validating, logging):

Observable.kt
import kotlin.properties.Delegates

var name: String by Delegates.observable("initial") { _, old, new ->
    println("Changed from $old to $new")
}

name = "Sara"      // fires the callback
name = "Omar"

Note

Other built-in delegates: Delegates.vetoable (reject a change by returning false), Delegates.notNull() (a non-null property set later), and delegating to a map (handy for dynamic/JSON-like data). You can even write your own delegate — it's just a class with getValue/setValue operators.

Recap & quick check

Key takeaways

  • Delegation forwards work to another object; Kotlin's 'by' keyword makes it a language feature.
  • Class delegation: 'class X : Interface by obj' implements the interface by forwarding to obj (no boilerplate).
  • Delegated properties hand a property's get/set to a helper via 'by'.
  • by lazy { } computes once on first access and caches the result (thread-safe).
  • Delegates.observable fires a callback on change (old, new); vetoable can reject changes.

Quick check

1. What does 'class Logger(p: Printer) : Printer by p' do?

2. What does 'by lazy { }' do?

3. Is 'by lazy' thread-safe by default?

4. What does Delegates.observable provide?

5. What keyword enables both class and property delegation?

Excellent — you've completed Phase 4! You command collections, sequences, generics, and delegation. Next comes Phase 5 — Advanced Kotlin.