Kotlin:如何获得成员属性的委托类?



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

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

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()
}

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

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]

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

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

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

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

最新更新