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

🧠 Test Your Knowledge

Which keyword is used for class delegation in Kotlin?