Delegation Workbook


Practice problems for The by Keyword: Delegation Without the Boilerplate. Each takes a minute or two. Write your own answer first, then click Show answer — nothing here is a trick question, just direct practice of the syntax from the lesson.

class delegation

1. Delegate an interface

Write class LoggingList<T>(private val inner: MutableList<T>) : MutableList<T> that forwards everything to inner but prints before each add.

Show answer Hide answer
class LoggingList<T>(
    private val inner: MutableList<T> = mutableListOf()
) : MutableList<T> by inner {
    override fun add(element: T): Boolean {
        println("adding $element")
        return inner.add(element)
    }
}

by inner generates every forwarding method; you override only what you want to change.

2. Delegate, then add methods

Write a Stack<T> that delegates MutableList<T> behavior to an inner list and adds push(x) and pop().

Show answer Hide answer
class Stack<T>(
    private val items: MutableList<T> = mutableListOf()
) : MutableList<T> by items {
    fun push(x: T) = items.add(x)
    fun pop(): T = items.removeLast()
}

by items forwards the whole MutableList surface; you write only what’s new — and it works even though items is composed, not inherited.

delegated properties

3. Compute once, lazily

Declare a val config that loads from disk on first access and caches the result.

Show answer Hide answer
val config: Config by lazy {
    loadConfigFromDisk()
}

The block runs at most once, on first read.

4. React to changes

Declare a var name that prints "<old> -> <new>" after each assignment.

Show answer Hide answer
import kotlin.properties.Delegates

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

5. Back a property with a map

Declare class Config(source: Map<String, Any?>) with a val host: String and val port: Int read from the map by their own names.

Show answer Hide answer
class Config(private val source: Map<String, Any?>) {
    val host: String by source
    val port: Int by source
}

custom delegates

6. Write a delegate

Write a delegate UpperCase so that var name: String by UpperCase() stores every assignment uppercased.

Show answer Hide answer
import kotlin.reflect.KProperty

class UpperCase {
    private var stored = ""
    operator fun getValue(thisRef: Any?, property: KProperty<*>) = stored
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        stored = value.uppercase()
    }
}

7. A read-only delegate

Write the minimal delegate Constant42 so that val answer: Int by Constant42() always reads as 42.

Show answer Hide answer
import kotlin.reflect.KProperty

class Constant42 {
    operator fun getValue(thisRef: Any?, property: KProperty<*>) = 42
}

val answer: Int by Constant42()

A read-only (val) delegate needs only getValue; a var would also need setValue.

lateinit vs lazy

8. Assigned from outside

You have a non-null var set by a framework after construction, not computed by the object itself. Which tool — and declare it for a repository: UserRepository.

Show answer Hide answer
lateinit var repository: UserRepository

lateinit — for a non-null var something else assigns later. lazy is for a val that computes itself.

9. Reject bad assignments

Declare a var age that refuses negative values (keeping the previous value) using Delegates.vetoable.

Show answer Hide answer
import kotlin.properties.Delegates

var age: Int by Delegates.vetoable(0) { _, _, new -> new >= 0 }

vetoable runs before the assignment and rejects it when the handler returns false.


Back to the lesson, The by Keyword, or on to the next one: generics.