允许在运行时添加观察器的可观察属性



通过Delegates.observable,Kotlin允许可观察的属性。但是,我需要在运行时添加观察者的能力,就像Java的Observable类一样。

我现在拥有的,如下:

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
}

它有效,但感觉就像重新发明轮子一样。

没有标准解决方案吗?

如果不是,我所做的是正确的吗?

同一想法的稍短变体:

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
}

输出符合预期。也许,最好将observers属性设为私有并添加一个添加订阅者的方法,但为了简单起见,我省略了它。

这是因为您从一个简单的示例开始,并且找不到 Kotlin 委托属性的好处。

Kotlin 不会强迫你实现任何接口来支持委托属性,yon 可以在 Kotlin 中使用委托属性,只需提供 getValue 和 setValue(?( 运算符。

Kotlin 从 1.1 开始提供了一个 provideDelegate 运算符函数,它允许您管理/控制如何创建委托。

Kotlin 中的委托在后台工作,这意味着从源代码的角度来看它是不可见的,并让代码源将委托的属性视为常规属性。

Kotlin 委托属性可以轻松让您在 Java 中管理 Java Bean,而无需在 Java 中使用 PropertyEditorSupport,并且您根本不需要在 Kotlin 中管理委托,只需通知更改的属性即可。 例如:

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

注意:getValue 和 setValue 运算符在下面是私有的。

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)
}
}

最新更新