允许在运行时添加观察者的Observable属性

时间:2017-07-19 14:51:23

标签: properties kotlin observable

通过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
}

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

对此没有标准解决方案吗?

如果没有,我的做法是否正确?

2 个答案:

答案 0 :(得分:1)

相同想法的略微变化:

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属性设为私有,并添加一个添加订阅者的方法,但为了简单起见我省略了它。

答案 1 :(得分:0)

这是因为你从一个简单的例子开始,并且无法找到Kotlin delegated properties的好处。

Kotlin并没有强迫您实现任何支持delegated properties的界面,您可以在Kotlin中使用delegated properties只提供getValue & setValue(?)运算符。他们的知名度甚至可以是私人

Kotlin自1.1以来提供了provideDelegate运算符功能,可让您管理/控制如何来创建委托。

Kotlin中的代表正在使用后台,这意味着从源代码的角度来看它是不可见,让代码源处理{{3作为常规属性。

Kotlin delegated properties可以轻松地让您在不使用Java中的delegated properties的情况下管理java bean,并且您根本不需要在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">]}

注意:PropertyEditorSupport运算符功能私有

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