Propiedad observable que permite agregar observadores en time de ejecución

A través de Delegates.observable , Kotlin permite properties observables. Sin embargo, necesito la capacidad de agregar observadores en time de ejecución, como lo hace la class Observable de Java.

Lo que tengo ahora es lo siguiente:

 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 } 

Funciona, pero parece redevise la rueda.

¿No hay una solución estándar para esto?

Si no, ¿es correcto lo que hice?

Una variante ligeramente más corta de la misma 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 } 

La salida es como se esperaba. Probablemente, es mejor hacer privada la propiedad de los observers y agregar un método para agregar suscriptores, pero lo omití por la simplicidad.

Esto se debe a que comienza con un ejemplo simple y no puede encontrar los beneficios de las properties delegadas de Kotlin.

Kotlin no te obliga a implementar ninguna interfaz para admitir properties delegadas ; no puedes usar las properties delegadas en Kotlin solo para proporcionar operadores getValue & setValue (?) . y su visibilidad incluso puede ser privada .

Kotlin proporcionó una function de operador provideDelegate desde 1.1, que le permite administrar / controlar cómo crear un delegado.

El delegado en Kotlin está trabajando en segundo plano , lo que significa que es invisible desde el punto de vista del código fuente, y permite que el código fuente trate las properties delegadas como properties regulares.

Las properties delegadas de Kotlin pueden permitirle administrar beans Java sin utilizar PropertyEditorSupport en Java, y no necesita administrar el delegado en Kotlin, solo para notificar la propiedad modificada. por ejemplo:

 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">]} 

Nota: el operador getValue & setValue funciona en privado a continuación.

 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) } } 
  • Kotlin: ¿Cómo puedo usar properties delegadas en Java?
  • Property setter con diferente tipo
  • ¿Cómo implementar una propiedad que proviene de una fuente determinada hasta que se establece directamente en Kotlin?
  • No se puede usar getter personalizado con propiedad delegada
  • Kotlin: cómo acceder a las properties en el constructor
  • Kotlin: ¿Cómo puedo evitar el autoboxing (basura) en las properties delegadas?
  • Función de extensión Kotlin en propiedad mutable
  • Convierta variables estáticas de Java a Kotlin