【问题标题】:Kotlin idiomatic way to forward property from different classKotlin 惯用的方式来转发来自不同类的属性
【发布时间】:2019-09-04 00:39:20
【问题描述】:

如示例:

class A {    
    var value: String
    get() = "value"
    set(value) { println(value) }
}

class B {
    val a = A()
    var value = a.value
}

fun main(args: Array<String>) {
    B().value = "new value"
}

B.value 只用A.value.get 初始化,所以A.value.setmain 函数的赋值过程中当然不会被调用,因此不会将消息打印到控制台。

我寻找的是允许将getset 函数转发到基于相同类型的不同对象属性的新变量的语法。

我设法实现了这样的委托,但它依赖于 kotlin-reflect 库(调用 gettersetter),我想避免这种情况:

import kotlin.reflect.KProperty
import kotlin.reflect.KMutableProperty

class Forward<T> (private val originalProperty: KProperty<T>){
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return originalProperty.getter.call()
    }
}

class ForwardMutable<T> (private val originalProperty: KMutableProperty<T>){
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        originalProperty.setter.call(value)
    }

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return originalProperty.getter.call()
    }
}

fun <T> forward(originalProperty: KProperty<T>) = Forward(originalProperty)
fun <T> forward(originalProperty: KMutableProperty<T>) = ForwardMutable(originalProperty)

class A {
    var value: String
        get() = "value"
        set(value) { println(value) }
}

class B {
    val a = A()
    var value by forward(a::value)
}

fun main(args: Array<String>) {
    B().value = "new value" // calls A.value.setter
}

没有为此目的的内置委托吗?如果没有,如何避免kotlin-reflect 依赖?

【问题讨论】:

    标签: generics kotlin reflection delegates


    【解决方案1】:

    也许我误解了一些东西,但是,如果您能够提取要委托给内部实例的变量的接口,那么为什么不直接使用委托呢?

    interface SomethingWithValues{
        var value: String
        var value2: String
        var value3: String
    }
    
    class A : SomethingWithValues{
        override var value: String = "value"
            set(value) {print(value)}
        override var value2: String = "value2"
            set(value) {print(value)}
        override var value3: String = "value3"
            set(value) {print(value)}
    }
    
    class B(val a:A = A()): SomethingWithValues by a
    
    fun main(args: Array<String>) {
        val b = B()
        b.value = "new value"
        b.value2 = "new value2"
        b.value3 = "new value3"
    }
    

    【讨论】:

    • 这是一个选项。但不幸的是,如果出现以下情况,它将不起作用: 1. 我没有班级A。 2. 我想用不同的名字装饰房产。
    • 好吧,问题 1 是不可避免的,但是问题 2……我很喜欢你的 ForwardMutable 解决方案。为什么没有反射?我的意思是你能想到不用任何语言反思就可以解决这个问题吗?即使 kotlin 库开箱即用地提供了这种类型的属性委托,我也很确定它会使用反射。
    • 不使用反射的原因有很多。在 Kotlin 中,它是静态绑定到委托属性的,所以还不错。但由于 Kotlin 是 JVM 语言(基本上解析为 Java),因此(有时)可以用静态代码替换动态代码。例如,Kotlin 可以有语法 var value: String watch a::value,它会生成 setter 和 getter java 代码,不涉及反射,但这只是我目前的梦想。
    • 是的,公平点,我忘记了这一切都是为了避免编写一些 kotlin 已经提供的语法糖;)
    • @xinaiz 我认为您现有的实现很好,缺少语言结构,如果您正在尝试建议编译时结构,那么也许可以在 jetbrains kotlin 论坛中提出它?
    【解决方案2】:

    如果thisRef 必须与Any 一样通用,那么我认为您无法在没有反射的情况下访问其属性。

    如果您愿意引入额外的接口,您可能可以按照以下方式做一些事情:(仍然存在对 kotlin.reflect 的导入依赖,但这是 Delegate API 所要求的)

    import kotlin.reflect.KProperty
    
    interface HasA<T> {
        val a: ValueAccessor<T>
    }
    
    interface ValueAccessor<T> {
        var value: T
    }
    
    class ForwardMutable<T> {
        operator fun setValue(thisRef: HasA<T>, property: KProperty<*>, value: T) {
            thisRef.a.value = value
        }
    
        operator fun getValue(thisRef: HasA<T>, property: KProperty<*>): T {
            return thisRef.a.value
        }
    }
    
    class A : ValueAccessor<String> {
        override var value: String = "value"
    }
    
    class B : HasA<String> {
        override val a = A()
        var value by ForwardMutable<String>()
    }
    
    fun main(args: Array<String>) {
        val b = B()
        b.value = "new value" // calls A.value.setter
        print(b.value)
    }
    

    【讨论】:

    • 谢谢,但我正在寻找更轻量级的方法。定义接口(基本上是合同)对于(似乎不是)简单的属性访问来说太多了。在最坏的情况下,我可以写var value: String get() = a.value; set(value) {a.value = value},但我想用委托来概括它。
    • 嗯,一般只有两种方法可以访问属性而不会出现编译错误 - a。通过暴露该属性的接口;湾。通过反思。选择你的毒药。 :-)
    猜你喜欢
    • 2014-09-11
    • 2018-12-06
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多