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: UserRepositorylateinit — 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.