Kotlin Delegation
Let other objects handle work for you automatically
🤝 What is Kotlin Delegation?
Delegation lets one object pass responsibilities to another object. Instead of inheriting behavior, you delegate tasks to helper objects, making code more flexible and reusable.
interface Printer {
fun print(message: String)
}
class ConsolePrinter : Printer {
override fun print(message: String) = println(message)
}
// Delegate printing to ConsolePrinter
class Document(printer: Printer) : Printer by printer
val doc = Document(ConsolePrinter())
doc.print("Hello Delegation!") // Hello Delegation!
Types of Delegation
Class Delegation
Delegate interface implementation
class Manager(worker: Worker) : Worker by worker
Property Delegation
Delegate property get/set operations
var name: String by Delegates.observable("") {
_, old, new -> println("$old -> $new")
}
Lazy Delegation
Initialize properties when first accessed
val expensiveValue: String by lazy {
println("Computing...")
"Expensive Result"
}
Observable Delegation
Watch property changes automatically
var score by Delegates.observable(0) {
_, old, new -> println("Score: $old -> $new")
}
🔹 Class Delegation
Delegate interface implementation to another object:
interface Engine {
fun start()
fun stop()
}
class PetrolEngine : Engine {
override fun start() = println("Petrol engine started")
override fun stop() = println("Petrol engine stopped")
}
class ElectricEngine : Engine {
override fun start() = println("Electric engine started")
override fun stop() = println("Electric engine stopped")
}
// Car delegates Engine behavior
class Car(engine: Engine) : Engine by engine {
fun drive() {
start() // Delegated to engine
println("Car is driving")
stop() // Delegated to engine
}
}
fun main() {
val petrolCar = Car(PetrolEngine())
petrolCar.drive()
val electricCar = Car(ElectricEngine())
electricCar.drive()
}
Output:
Petrol engine started
Car is driving
Petrol engine stopped
Electric engine started
Car is driving
Electric engine stopped
🔹 Lazy Properties
Initialize expensive properties only when needed:
class DataProcessor {
// Expensive computation - only runs when first accessed
val processedData: List by lazy {
println("Processing data... (this runs only once)")
Thread.sleep(1000) // Simulate expensive operation
listOf("Data1", "Data2", "Data3")
}
// Another lazy property
val summary: String by lazy {
println("Creating summary...")
"Processed ${processedData.size} items"
}
}
fun main() {
val processor = DataProcessor()
println("Processor created")
// First access - computation happens
println("First access: ${processor.processedData}")
// Second access - uses cached result
println("Second access: ${processor.processedData}")
// Summary uses the already computed data
println(processor.summary)
}
Output:
Processor created
Processing data... (this runs only once)
First access: [Data1, Data2, Data3]
Second access: [Data1, Data2, Data3]
Creating summary...
Processed 3 items
🔹 Observable Properties
Watch property changes automatically:
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("Unknown") { property, oldValue, newValue ->
println("${property.name} changed from '$oldValue' to '$newValue'")
}
var age: Int by Delegates.observable(0) { _, old, new ->
println("Age updated: $old -> $new")
if (new >= 18) {
println("User is now an adult!")
}
}
}
fun main() {
val user = User()
user.name = "Alice" // Triggers observer
user.name = "Bob" // Triggers observer
user.age = 16 // Triggers observer
user.age = 18 // Triggers observer + adult message
}
Output:
name changed from 'Unknown' to 'Alice'
name changed from 'Alice' to 'Bob'
Age updated: 0 -> 16
Age updated: 16 -> 18
User is now an adult!
🔹 Custom Delegates
Create your own property delegates:
import kotlin.reflect.KProperty
// Custom delegate that stores values in uppercase
class UppercaseDelegate {
private var value: String = ""
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
this.value = value.uppercase()
println("Set ${property.name} to: ${this.value}")
}
}
class Person {
var firstName: String by UppercaseDelegate()
var lastName: String by UppercaseDelegate()
val fullName: String
get() = "$firstName $lastName"
}
fun main() {
val person = Person()
person.firstName = "john" // Stored as "JOHN"
person.lastName = "doe" // Stored as "DOE"
println("Full name: ${person.fullName}")
}
Output:
Set firstName to: JOHN
Set lastName to: DOE
Full name: JOHN DOE