【问题标题】:How to combine kotlin delegated property: observable, vetoable, and "by map"?如何结合 kotlin 委托属性:可观察、可否决和“按地图”?
【发布时间】:2018-10-15 02:55:13
【问题描述】:

我正在尝试将 delegates/observablevetoable 结合使用(查看源代码 kotlin.properties.Delegates.kt 后这不是问题),但在尝试同时使用 store the properties in a map 时事情变得很棘手。

或者换句话说,如何将这三者结合起来:

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

【问题讨论】:

    标签: kotlin delegates kotlin-delegate


    【解决方案1】:

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

    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}
    

    注意:如果你想要地图中的默认值,请自己放在那里

    【讨论】:

    • 我认为这行得通!我很好奇:重用现有的 ObservableProperty.beforeChange 是否更有意义?或者这就是“财产”和“通过地图获取财产”之间的交叉点?
    • @BenjaminH 确切地说,它们在内部缓存属性值,并防止手动对地图所做的更改在属性中可见。如果你不碰地图,我猜你可以让它工作,例如更改写入地图
    猜你喜欢
    • 2023-04-02
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    • 2018-06-11
    • 1970-01-01
    • 2014-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多