Kotlin Custom Delegates
A custom delegate is a class with operator functions named getValue and setValue that run whenever a property is read or written. The property keeps a plain type while the delegate class holds the value and the extra behavior.
A delegate that reports each write
Tracked holds an int and prints a line inside setValue on each write. Meter reads 10 first, then the write prints its line, then the new value reads back.
class Tracked(private var value: Int) {
operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Int {
return value
}
operator fun setValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>, newValue: Int) {
println("set " + property.name + " to " + newValue)
value = newValue
}
}
class Meter {
var reading: Int by Tracked(10)
}
fun main() {
val meter = Meter()
println(meter.reading)
meter.reading = 25
println(meter.reading)
}The property name travels with the call
The same Tracked class serves a property named celsius here. The set line names celsius, read from the property handed to setValue.
class Tracked(private var value: Int) {
operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Int {
return value
}
operator fun setValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>, newValue: Int) {
println("set " + property.name + " to " + newValue)
value = newValue
}
}
class Thermometer {
var celsius: Int by Tracked(20)
}
fun main() {
val thermometer = Thermometer()
println(thermometer.celsius)
thermometer.celsius = 30
println(thermometer.celsius)
}Two properties, two delegate objects
Gauge holds level and peak, each backed by its own Tracked object. Writing level prints its own name and leaves peak untouched.
class Tracked(private var value: Int) {
operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Int {
return value
}
operator fun setValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>, newValue: Int) {
println("set " + property.name + " to " + newValue)
value = newValue
}
}
class Gauge {
var level: Int by Tracked(1)
var peak: Int by Tracked(9)
}
fun main() {
val gauge = Gauge()
println(gauge.level)
println(gauge.peak)
gauge.level = 4
println(gauge.level)
}Example
class Tracked(private var value: Int) {
operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Int {
return value
}
operator fun setValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>, newValue: Int) {
println("set " + property.name + " to " + newValue)
value = newValue
}
}
class Score {
var points: Int by Tracked(0)
}
fun main() {
val score = Score()
println(score.points)
score.points = 5
score.points = 12
println(score.points)
}Key points
- A read of the property runs the delegate getValue function.
- A write to the property runs the delegate setValue function with the new value.
- Both functions receive the property itself, typed kotlin.reflect.KProperty<*>.
- The property name is available inside the delegate as property.name.
- The class header needs no special marker, since the operator functions are the contract.
In this example, Score delegates its points property to a Tracked object that starts at 0. Each assignment runs setValue, which prints the property name and the new value before storing it. The final read returns 12 from the delegate.
Note
The reflect type is named in full here, so the program needs no import line.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Kotlin Playground
Exercise
Which pair of operator functions turns a class into a property delegate?