链接 属性 引用并保持读写能力
Chaining property references and maintaining both the ability to read and write
使用 KMutableProperty1
访问 类 属性 既可以用作 getter 也可以用作 setter。
class BaseClass(
var baseInt: Int = 0,
var baseInnerClass: InnerClass = InnerClass()
)
class InnerClass(
var innerInt: Int = 0,
)
val target = BaseClass()
val kMutableProperty1b = (BaseClass::baseInt)
kMutableProperty1b.set(target, 4)
val baseInt = kMutableProperty1b.get(target)
能够访问像
这样的嵌套属性
BaseClass::innerClass -> InnerClass:innerInt
我试着用
链接两个kMutableProperty1
fun <A, B, C> ((A) -> B).chained(getter : (B) -> C) : (A) -> C = { getter(this(it)) }
这样,可以读取内部属性,但不能设置:
val chainedKMutableProperty = baseMutableProperty.chained(InnerClass::innerInt)
val innerInt = chainedKMutableProperty(target)
chainedKMutableProperty.set(target, 5) // Not available
在Swift中可以使用KeyPaths
实现类似的东西
let target = BaseClass()
let aKeyPath = \BaseClass.baseInt
target[keyPath: aKeyPath] = 4
let baseInt = target[keyPath: aKeyPath]
let bKeyPath = \BaseClass.baseInnerClass
let chainedKeyPath = bKeyPath.appending(path: \InnerClass.innerInt)
let innerInt = target[keyPath: chainedKeyPath]
target[keyPath: chainedKeyPath] = 5
我如何在 Kotlin 中做同样的事情 - 链接 属性 访问器并保持读写能力?
我认为 Kotlin 或 Java stdlib 中没有这样的东西。我们可以很容易地自己创建它,尽管我认为坚持 KProperty
并不是一个好主意。这个接口不仅仅是一个通用的访问器接口。这是一件非常具体的事情:属性 的 class。我们在这里不处理 class 属性。
相反,我建议创建我们自己的界面。下面是一个简单的 POC:
fun main() {
val target = BaseClass()
val chainedProp = BaseClass::baseInnerClass chain InnerClass::innerInt
println(chainedProp.get(target))
chainedProp.set(target, 5)
// or
println(target[chainedProp])
target[chainedProp] = 12
}
operator fun <T, V> T.get(key: MyProperty<T, V>): V = key.get(this)
operator fun <T, V> T.set(key: MyMutableProperty<T, V>, value: V) = key.set(this, value)
infix fun <T, V, V2> KProperty1<T, V>.chain(next: KMutableProperty1<V, V2>): MyMutableProperty<T, V2> = asMyProperty() chain next.asMyProperty()
infix fun <T, V, V2> MyProperty<T, V>.chain(next: MyMutableProperty<V, V2>): MyMutableProperty<T, V2> = object : MyMutableProperty<T, V2> {
override fun get(receiver: T): V2 {
return next.get(this@chain.get(receiver))
}
override fun set(receiver: T, value: V2) {
next.set(this@chain.get(receiver), value)
}
}
fun <T, V> KProperty1<T, V>.asMyProperty(): MyProperty<T, V> = object : MyProperty<T, V> {
override fun get(receiver: T): V {
return this@asMyProperty.get(receiver)
}
}
fun <T, V> KMutableProperty1<T, V>.asMyProperty(): MyMutableProperty<T, V> = object : MyMutableProperty<T, V> {
override fun get(receiver: T): V {
return this@asMyProperty.get(receiver)
}
override fun set(receiver: T, value: V) {
this@asMyProperty.set(receiver, value)
}
}
interface MyProperty<in T, out V> {
fun get(receiver: T): V
}
interface MyMutableProperty<in T, V> : MyProperty<T, V> {
fun set(receiver: T, value: V)
}
使用 KMutableProperty1
访问 类 属性 既可以用作 getter 也可以用作 setter。
class BaseClass(
var baseInt: Int = 0,
var baseInnerClass: InnerClass = InnerClass()
)
class InnerClass(
var innerInt: Int = 0,
)
val target = BaseClass()
val kMutableProperty1b = (BaseClass::baseInt)
kMutableProperty1b.set(target, 4)
val baseInt = kMutableProperty1b.get(target)
能够访问像
这样的嵌套属性BaseClass::innerClass -> InnerClass:innerInt
我试着用
链接两个kMutableProperty1
fun <A, B, C> ((A) -> B).chained(getter : (B) -> C) : (A) -> C = { getter(this(it)) }
这样,可以读取内部属性,但不能设置:
val chainedKMutableProperty = baseMutableProperty.chained(InnerClass::innerInt)
val innerInt = chainedKMutableProperty(target)
chainedKMutableProperty.set(target, 5) // Not available
在Swift中可以使用KeyPaths
实现类似的东西let target = BaseClass()
let aKeyPath = \BaseClass.baseInt
target[keyPath: aKeyPath] = 4
let baseInt = target[keyPath: aKeyPath]
let bKeyPath = \BaseClass.baseInnerClass
let chainedKeyPath = bKeyPath.appending(path: \InnerClass.innerInt)
let innerInt = target[keyPath: chainedKeyPath]
target[keyPath: chainedKeyPath] = 5
我如何在 Kotlin 中做同样的事情 - 链接 属性 访问器并保持读写能力?
我认为 Kotlin 或 Java stdlib 中没有这样的东西。我们可以很容易地自己创建它,尽管我认为坚持 KProperty
并不是一个好主意。这个接口不仅仅是一个通用的访问器接口。这是一件非常具体的事情:属性 的 class。我们在这里不处理 class 属性。
相反,我建议创建我们自己的界面。下面是一个简单的 POC:
fun main() {
val target = BaseClass()
val chainedProp = BaseClass::baseInnerClass chain InnerClass::innerInt
println(chainedProp.get(target))
chainedProp.set(target, 5)
// or
println(target[chainedProp])
target[chainedProp] = 12
}
operator fun <T, V> T.get(key: MyProperty<T, V>): V = key.get(this)
operator fun <T, V> T.set(key: MyMutableProperty<T, V>, value: V) = key.set(this, value)
infix fun <T, V, V2> KProperty1<T, V>.chain(next: KMutableProperty1<V, V2>): MyMutableProperty<T, V2> = asMyProperty() chain next.asMyProperty()
infix fun <T, V, V2> MyProperty<T, V>.chain(next: MyMutableProperty<V, V2>): MyMutableProperty<T, V2> = object : MyMutableProperty<T, V2> {
override fun get(receiver: T): V2 {
return next.get(this@chain.get(receiver))
}
override fun set(receiver: T, value: V2) {
next.set(this@chain.get(receiver), value)
}
}
fun <T, V> KProperty1<T, V>.asMyProperty(): MyProperty<T, V> = object : MyProperty<T, V> {
override fun get(receiver: T): V {
return this@asMyProperty.get(receiver)
}
}
fun <T, V> KMutableProperty1<T, V>.asMyProperty(): MyMutableProperty<T, V> = object : MyMutableProperty<T, V> {
override fun get(receiver: T): V {
return this@asMyProperty.get(receiver)
}
override fun set(receiver: T, value: V) {
this@asMyProperty.set(receiver, value)
}
}
interface MyProperty<in T, out V> {
fun get(receiver: T): V
}
interface MyMutableProperty<in T, V> : MyProperty<T, V> {
fun set(receiver: T, value: V)
}