【问题标题】:Kotlin: How can I get the delegation class of a member property?Kotlin:如何获取成员属性的委托类?
【发布时间】:2017-01-18 04:42:12
【问题描述】:

如何获取成员属性的委托类?

我的意思是说有没有可能完成这样的功能:

inline fun <reified T> delegationExample(t: T) {
    for (prop in T::class.declaredMemberProperties) {
        val delegatedClass = // what to do?!
    }
}

委托类可能如下所示:

class DelegationExample {
    operator fun getValue(ref: Any, prop: KProperty<*>) = 0
}

声明类可能如下所示:

object Example {
    val a by DelegationExample()
    val b by DelegationExample()
    val c by DelegationExample()
}

【问题讨论】:

    标签: properties delegates kotlin member delegation


    【解决方案1】:

    要查找委托给委托类的属性以及该类的实例,这里有一个实用函数:

    data class DelegatedProperty<T : Any, DELEGATE : Any>(val property: KProperty1<T, *>, val delegatingToInstance: DELEGATE)
    
    inline fun <reified T : Any, DELEGATE : Any> findDelegatingPropertyInstances(instance: T, delegatingTo: KClass<DELEGATE>): List<DelegatedProperty<T, DELEGATE>> {
        return T::class.declaredMemberProperties.map { prop ->
            val javaField = prop.javaField
            if (javaField != null && delegatingTo.java.isAssignableFrom(javaField.type)) {
                javaField.isAccessible = true // is private, have to open that up
                @Suppress("UNCHECKED_CAST")
                val delegateInstance = javaField.get(instance) as DELEGATE
                DelegatedProperty(prop, delegateInstance)
            } else {
                null
            }
        }.filterNotNull()
    }
    

    几点说明:

    • 首先将您的具体类型T 更正为T: Any,否则您将无法访问Kotlin 反射中的所有扩展,包括declaredMemberProperties
    • 最简单的方法是从属性引用中获取该字段,以确保您实际上在谈论的是真正的属性,因此对于每个declaredMemberProperties 使用javaField 这样做。
    • 由于javaField 是一个自定义getter 并且可以为空,它被保存到一个局部变量中,所以智能转换将在以后工作。
    • 然后,如果此字段与您要查找的委托类具有相同的类型,则您可以访问该字段。
    • 但首先您必须强制该字段的可访问性,因为它是一个private 字段。

    在测试程序中运行:

    class DelegationExample {
        operator fun getValue(ref: Any, prop: KProperty<*>) = 0
    }
    
    class Example {
        val a by DelegationExample()
        val b by DelegationExample()
        val c by DelegationExample()
    }
    
    fun main(args: Array<String>) {
        findDelegatingPropertyInstances(Example(), DelegationExample::class).forEach {
            println("property '${it.property.name}' delegates to instance of [${it.delegatingToInstance}]")
        }
    }
    

    输出类似于:

    property 'a' delegates to instance of [DelegationExample@2c1b194a]
    property 'b' delegates to instance of [DelegationExample@4dbb42b7]
    property 'c' delegates to instance of [DelegationExample@66f57048]
    

    【讨论】:

    • 当然,两者都做类似的事情。我想返回在这个例子中委托的所有东西,也不要在我不需要的任何东西上调用isAccessible(调用它有影响吗?)
    • 您引用的补丁 (Jire) 仅在只有一个声明的属性使用作为第二个参数传递的委托的情况下才有效。当有 2 个或更多属性使用同一个委托时,是否仍然可以找到给定属性的委托?我很想有一个可以做到这一点的功能。我试图弄清楚,但我认为这是不可能的。
    【解决方案2】:

    在字节码级别委托属性不延迟常规属性(公共 getter/setter 和私有字段)。

    您可以采取的一种方法是扫描Example 的私有字段并过滤那些具有operator getValue(thisRef: R, KProperty&lt;*&gt;) 的字段。从技术上讲,一个字段可能包含一个委托对象val x = lazy {1},但这不太可能。

    【讨论】:

      【解决方案3】:

      避免反射的一种方法是首先初始化您的委托对象并将其存储为它自己的成员,然后通过它委托您的属性。

      object Example {
          val aDelegate = DelegationExample()
          val bDelegate = DelegationExample()
          val cDelegate = DelegationExample()
      
          val a by aDelegate
          val b by bDelegate
          val c by cDelegate
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-18
        • 1970-01-01
        • 2018-10-04
        • 2017-12-22
        • 1970-01-01
        • 2015-09-02
        • 1970-01-01
        • 2017-07-14
        相关资源
        最近更新 更多