首页 > 解决方案 > 访问接口类型变量的实现属性

问题描述

我正在尝试访问id类 ( FooImpl) 的属性 ( ) 的委托。问题是,这个类实现了一个接口 ( Foo),并且有问题的属性覆盖了这个接口的一个属性。委托只存在于类中(不能存在于接口中)。

问题是::在类型变量上使用运算符Foo总是返回 的属性Foo,而不是实际实例的属性。代码中的问题:

import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible

interface Foo {
    val id: Int
}

class FooImpl(
    id: Int,
) : Foo {
    override val id: Int by lazy { id }
}

val <T> KProperty<T>.hasDelegate: Boolean
    get() = apply { isAccessible = true }.let { (it as KProperty0<T>).getDelegate() != null }

fun main() {
    val foo: Foo = FooImpl(1)
    println("foo::id.hasDelegate = ${foo::id.hasDelegate}")
    println("(foo as FooImpl)::id.hasDelegate = ${(foo as FooImpl)::id.hasDelegate}")
}

这打印:

foo::id.hasDelegate = false
(foo as FooImpl)::id.hasDelegate = true

但这需要正确实现的编译时知识。我正在寻找的是访问正确的属性而无需在FooImpl那里指定。

该信息在运行时存在,因为到目前为止我发现的最少(!)侵入性解决方法是添加和访问属性fun idProp(): KProperty0<*>,并使用它访问属性。Foooverride fun idProp() = ::idFooImpl

还有比这更好的方法吗?

标签: kotlinreflectionkotlin-reflect

解决方案


我想出了这个,但我不知道是否有更好的方法。要解决的问题是getDelegate()必须返回委托的实际实例,因此您需要类的实例才能检索委托实例。如果有一个hasDelegate内置的属性,那就太好了。您的版本hasDelegate将在未绑定的 KProperty1 上的强制转换中崩溃,这是我们在特定类未知时必须处理的全部内容。

因此,要检索委托实例,我们需要按名称搜索类实例的成员属性,这为我们提供了具有超类类型的协变类类型的 KProperty。由于它是协变的,我们可以调用一个消费函数,就像getDelegate()不强制转换为不变类型一样。我认为这在逻辑上应该是安全的,因为我们正在传递一个我们知道具有与::class我们检索属性的匹配类型的实例。

@Suppress("UNCHECKED_CAST")
fun <T: Any> KProperty1<T, *>.isDelegated(instance: T): Boolean =
    (instance::class.memberProperties.first { it.name == name } as KProperty1<T, *>).run {
        isAccessible = true
        getDelegate(instance) != null
    }

fun main() {
    val foo: Foo = Foo2()
    println("foo::id.hasDelegate = ${Foo::id.isDelegated(foo)}")
}

推荐阅读