Delegated properties Observable

Hassle-free listeners with Observable

Posted on by Andras Kindler

Listening to the event of a property being changed is a highly common task. Let's say I have a variable, and somewhere in my code I want to be notified when it changes. There are a lot of ways to tackle this, but most of them require variable amounts of extra code (think Rx Subjects, or writing your own listener / observer), and are a bit too robust for the simplest use cases. Of course, Kotlin comes to the rescue with a built-in language feature - enter the observable property!

Observable

An observable (not to be confused with the Rx Observable) property is basically a regular property, with an initial value and a callback function that gets called anytime its value changes. Important detail: the function is called after the assignment was performed. A minimalist example:

class Book {
    var title: String by observable("untitled") { _, oldValue, newValue ->
        // do stuff
    }
}

The callback is called every time the title of a Book instance is updated (what an accurate real-life example!). Turning this into a "regular" listener is very straightforward.

The Book class, with the observable property:

class Book {
    var title: String by observable("untitled") { _, oldValue, newValue ->
        onTitleChanged?.invoke(oldValue, newValue)
    }

    var onTitleChanged: ((String, String) -> Unit)? = null
}

And setting a listener:

val book = Book()
book.onTitleChanged = { oldValue, newValue ->
    // do stuff
}

Super simple! Using the built-in Delegate.observable makes this happen with only a couple of lines of code, which is more concise (not to mention it is way more descriptive) than implementing a setter and calling it from a setter. Of course this is not a silver bullet, it shouldn't replace Rx Subjects in more complex use cases, but it works prefectly for simpler problems.

Nothing special happens under the hood. After decompiling, we can see that a delegate class is generated (a subclass of ObservableProperty), and is used in the Book class instead of a String, delegating accessors (getter and setter functions) to the generated class.

Vetoable

It is worth to mention that observable has a counterpart called the vetoable, which also includes a callback function, but allows the code to reject ("veto") the new value. The callback is invoked before the value is changed, and only does so when the function returns true, otherwise the value remains the same.

The following example will only update the book title if it is not an empty String.

class Book {
    var title: String by vetoable("untitled") { _, oldValue, newValue ->
        !newValue.isEmpty()
    }
}

Delegated Properties

As I mentioned before, by observable() is a member of the Delegates object. A delegated property means the get()/set() functions are delegated in a way that allows the object to perform an action when accessing or updating the value. The contents of this package is available on the JVM and JS.

This package contains a handful of factory methods for common use cases (also offering lazy in addition to observable and vetoable). It is also possible to roll your own delegated property, both for read-only (implementing the ReadOnlyProperty interface) and mutable (implementing the ReadWriteProperty interface). This is one of the reasons why using Kotlin is so powerful - a lot of common problems are already implemented and available for immediate use.