Learning Kotlin: The Observable Delegate (with a slight detour on reference functions)

Note * This is the 27th post in a multipart series. If you want to read more, see our series index * Koans used here are 34 and 35

Following on from our introduction to the by operator and delegate properties, this post looks at the second of the five built-in delegates, observable. This delegate allows you to intercept all attempts to set the value. You can achieve this with a setter, but remember you would need to duplicate that setter code every time. With observable, you can build the logic once and reuse it over and over again.

Once again, let’s start with how we would do this without the delegated property:

class User() {
    var name: String = ""
        set(value) { DataChanged("name", name, value) }

    var eyeColour: String = ""
        set(value) { DataChanged("eyeColour", name, value) }

    fun DataChanged(propertyName: String, oldValue: String, newValue: String) {
        println("$propertyName changed! $oldValue -> $newValue")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "Robert"
    user.eyeColour = "Green"
}

Note: We need to manually define the setter twice, and in each, we must change a value—something you’ll likely overlook when copy-pasting the code.

In the next example, we switch to the observable delegate, which lets us call the same function easily. While I don’t recommend this for production, I did demonstrate two different ways of using it in this example.

For age, since the second parameter is a lambda, I created one and passed the parameters to my function—this is how most demos show usage. For name, however, because my function has the same signature as the lambda, I could pass it directly to observable, which I find much nicer. However, since we need to pass a reference to our function, we must prefix it with ::.

package sadev

import kotlin.properties.Delegates
import kotlin.reflect.KProperty

class User() {
    var name: String by Delegates.observable("", ::DataChanged)
    var eyeColour: String by Delegates.observable("") { property, old, new ->
        DataChanged(property, old, new)
    }

    fun DataChanged(property: KProperty, oldValue: String, newValue: String) {
        println("${property.name} changed! $oldValue -> $newValue")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "Robert"
    user.eyeColour = "Green"
}