An observable property that allows you to add observers at runtime

Through Delegates.observable

, Cottin admits observable properties. I need, however, the ability to add observers at runtime, as the Java class does Observable

.

Now I have the following:

import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible


class MyObservable<T> (var v: T): java.util.Observable() {

    operator fun getValue(thisRef: Any, prop: KProperty<*>) = v
    operator fun setValue(thisRef: Any, prop: KProperty<*>, newValue: T) {
        v = newValue
        setChanged()
        notifyObservers()
    }
}

fun <T> addObserver(prop: KProperty0<T>, observerFn: (T) -> Unit) =
        (prop.apply{ isAccessible = true }.getDelegate() as MyObservable<T>)
                .addObserver(Observer({ o, _ -> observerFn((o as MyObservable<T>).v) }))


class ObservableExample {
    var i: Int by MyObservable(3)
}

fun main(args: Array<String>) {
    val ex: ObservableExample = ObservableExample();

    addObserver(ex::i, { println(it) })

    ex.i = 7
    ex.i = 9

    // prints:
    // 7
    // 9
}

      

It works, but it looks like he is reinventing the wheel.

Is there no standard solution for this?

If not, what did I do right?

+3


source to share


2 answers


A slightly shorter version of the same idea:

import kotlin.properties.Delegates

typealias IntObserver = (Int) -> Unit

class ObservableExample {
    val prop1Observers = mutableListOf<IntObserver>()

    var prop1: Int by Delegates.observable(0) { prop, old, new ->
        prop1Observers.forEach { it(new) }
    }
}

fun main(args: Array<String>) {
    val example = ObservableExample()
    example.prop1Observers.add({ println(it) })
    example.prop1 = 1
    example.prop1 = 2
}

      



The output runs as expected. It's probably better to make the property observers

private and add a method to add subscribers, but I've left it out for simplicity.

+1


source


This is because you are starting with a simple example and cannot find the benefits of Kotlin's delegated properties .

Kotlin doesn't force you to implement any interface to support delegated properties , yon can use delegated properties in Kotlin just provide getValue and setValue (?) . and their visibility may even be private .

Kotlin has provided to provide a Delegate with an operator function since version 1.1 that allows you to control / control how the delegate is created.

A delegate in Kotlin runs in the background , which means invisible from the point of view of the source code and allows the code source to access the delegated properties as regular properties.

Kotlin delegated properties can easily allow you to manage java beans without using PropertyEditorSupport in Java, and you don't need to manage the delegate in Kotlin at all, just to notify the changed property. eg:



val history = mutableMapOf<String, MutableList<Pair<Any?, Any?>>>()
val subject = Subject()

subject.subscribe { event ->
    val each = history.getOrPut(event.propertyName) { mutableListOf() }
    each.add(event.oldValue to event.newValue)
}

//      v--- treat a delegated property as regular property
subject.number = 1
subject.string = "bar"
subject.number = 2

println(history);
//      ^--- {"number":[<null,1>,<1,2>], "string": [<null,"bar">]}

      


Note. getValue and setValue functions ...

class Subject {
    //                     v--- manage the delegated property internally
    var string: String? by this
    var number: Int? by this

    private val properties by lazy {
        mutableMapOf<Any?, Any?>()
    }
    private val listeners by lazy {
        mutableListOf<PropertyChangeListener>()
    }

    private operator @Suppress("UNCHECKED_CAST")
    fun <T : Any?> getValue(self: Any, prop: KProperty<*>): T {
        return properties[prop.name] as T
    }

    private operator 
    fun <T : Any?> setValue(self: Any,prop: KProperty<*>, newValue: T) {
        val event = PropertyChangeEvent(
                self,
                prop.name,
                properties[prop.name],
                newValue
        )
        properties[prop.name] = newValue
        listeners.forEach { it.propertyChange(event) }
    }

    fun subscribe(listener: (event: PropertyChangeEvent) -> Unit) {
        subscribe(PropertyChangeListener { listener(it) })
    }

    fun subscribe(subscriber: PropertyChangeListener) {
        listeners.add(subscriber)
    }
}

      

0


source







All Articles