Implementation of observables

How are observable variables implemented efficiently “under the hood”, i.e. at a low level, by the compiler?

I’m merely curious about the general approach, not detailed code.

Observables are implemented using property delegation:
https://kotlinlang.org/docs/reference/delegated-properties.html

The getter and setter get delegated to a special class that implements some sort of behavior (lazy, obeservable, etc). In most cases they are just a light weight class with a property storing the data and a special getter and optional setter function.
When you use a delegate the compiler creates a field with the delegate class and the getter/setter just call that class.

So, in effect, when an observable “x” is modified, e.g., “x = y”, the compiler generates the code needed to notify its watcher(s) of the event?

class Foo {
   val foo by Delegates.observable("foo") { old, new -> println("$old -> $new") }
}

This code gets compiled to something that is similar to this

class Foo {
   private val foo$delegate = Delegates.observable("foo") { old, new -> println("$old -> $new") }
   val foo: String
      set(value) { foo$delegate.setValue(this,  this::foo, value) }
      get() = foo$delegate.getValue(this, this::foo)
}

The delegate is implemented in the files I linked above, but it is pretty basic and just calls the callback whenever the setter is called.

1 Like

Thanks. But I’m still struggling to understand delegation. I was looking for a more generic explanation, without language-specific features or code details.

fun <T>Delegates.observable(initValue: T, onChange: (T, T) -> Unit) = object : ReadWriteProperty {
    private val value = initValue
    fun setValue(newValue: T) {
        val old = value
        value = newValue
        onChange.invoke(old, newValue)
    }
    fun getValue(): T = value
}

class Foo {
   private val fooDelegate = Delegates.observable("foo") { old, new -> 
       println("$old -> $new") 
   }
   val foo: String
      set(value) { fooDelegate.setValue(value) }
      get() = fooDelegate.getValue()
}

val foo = Foo()
println(foo.foo)   // prints "foo"
foo.foo = "bar"   // prints "foo -> bar"
println(foo.foo)  // prints "bar"

The Foo class is a simplified version of what the kotlin compiler generates when you use the code from my first example.

I can’t really simplify the code more than that. When you call foo.foo it calls the getter of the delegate instance, same for foo.foo = "bar". The setter just calls the lambda you pass to the delegate in the first place.

Is it possible to designate a variable “foo” as observed, and then simply do “foo = bar”?

Wasabi375, thanks for your help. I think I understand.

Another way to think about it is that a property is nothing but a field, a getter, and a setter, so whenever you call foo = bar, you are actually calling setFoo(bar), the setFoo method can do absolutely anything that it wants, but in this example, it just calls the delegate’s setValue method.