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
lazyfor 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:
Your class
Logger
A delegate
ConsolePrinter
Result
calls forwarded, zero boilerplate
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 automaticallyTip
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:
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 recomputationobservable
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):
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
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.