如何结合 kotlin delegated 属性: observable, vetoable, and "by map"?
How to combine kotlin delegated property: observable, vetoable, and "by map"?
我正在尝试合并 delegates/observable with vetoable (which isn't a problem after looking at the source kotlin.properties.Delegates.kt), but things got hairy when trying to also 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
没有简单的方法来编写委托,但您可以编写自己的委托,例如:
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}
注意:如果您想要地图中的默认值,请自行将它们放在那里
我正在尝试合并 delegates/observable with vetoable (which isn't a problem after looking at the source kotlin.properties.Delegates.kt), but things got hairy when trying to also 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
没有简单的方法来编写委托,但您可以编写自己的委托,例如:
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}
注意:如果您想要地图中的默认值,请自行将它们放在那里