如何结合Kotlin委托属性:可观察,否决和“按地图”?

时间:2018-10-15 02:55:13

标签: kotlin delegates kotlin-delegate

我正在尝试将delegates/observablevetoable结合使用(在查看源代码kotlin.properties.Delegates.kt后没有问题),但是在尝试同时{ {3}}。

或者换句话说,如何结合这三个:

var k1: Int by Delegates.observable(0) { property, oldValue, newValue ->
    println("Hi from k1 observer")
}

var k2:Int by Delegates.vetoable(0) {property, oldValue, newValue ->
    println("Hi from k2 more-than check")
     oldValue > newValue
}

val myMap = mutableMapOf<String, Int>()
var k3 by myMap

1 个答案:

答案 0 :(得分:3)

没有简单的方法可以组成委托,但是您可以编写自己的委托,例如:

inline fun <T> mapVetoObserver(
        map: MutableMap<String, T>,
        absentValue: T,
        crossinline veto: ((property: KProperty<*>, oldValue: T, newValue: T) -> Boolean),
        crossinline observe: ((property: KProperty<*>, oldValue: T, newValue: T) -> Unit))
        : ReadWriteProperty<Any?, T> {

    return object : ReadWriteProperty<Any?, T> {

        // there is no good way to set the map value to some initial value upon delegate
        // construction since the property name is unknown until getValue/setValue are called
        // => absent value rather than initial value

        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return map[property.name] ?: absentValue
        }

        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            val oldValue = getValue(thisRef, property)
            if (!veto(property, oldValue, value)) {
                map[property.name] = value
                observe(property, oldValue, value)
            }
        }
    }
}

然后像

一样使用它
val myMap = mutableMapOf<String, Int>()
var k1: Int by mapVetoObserver(myMap, 0,
        { _, oldValue, newValue ->
            (oldValue > newValue).also {
                println("veto: $oldValue => $newValue = $it")
            }
        },
        { property, oldValue, newValue ->
            println("${property.name} changed from $oldValue to $newValue")
        })
var k2: Int by mapVetoObserver(myMap, 42,
        { _, oldValue, newValue ->
            (oldValue > newValue).also {
                println("veto: $oldValue => $newValue = $it")
            }
        },
        { property, oldValue, newValue ->
            println("${property.name} changed from $oldValue to $newValue")
        })

一些用法示例:

println("k1: $k1") // k1: 0
println("k2: $k2") // k2: 42
// absentValue isn't in map
println(myMap)     // {}

k1 = 5             // veto: 0 => 5 = false
                   // k1 changed from 0 to 5
println("k1: $k1") // k1: 5
k1 = 3             // veto: 5 => 3 = true
println("k1: $k1") // k1: 5
// changes propagate to the map
println(myMap)     // {k1=5}
// this circumvents veto
myMap["k1"] = 3
println("k1: $k1") // k1: 3
println(myMap)     // {k1=3}

注意:如果您要在地图中使用默认值,请自己将它们放在此处