Kotlin中如何在接口类型变量上访问实现类的属性委托?
问题根因
Kotlin 的绑定属性引用(::运算符)是静态编译期解析的,你用编译期类型为Foo的变量获取id属性引用时,拿到的永远是Foo接口定义的抽象属性引用,自然不存在实现类才有的委托逻辑,这是 Kotlin 可调用引用的设计规则,不是运行时异常或者逻辑错误。
最优无侵入解决方案
不需要修改原有接口、实现类的任何代码,直接通过运行时反射获取实例实际类型的属性即可,通用实现如下:
import kotlin.reflect.KProperty1 import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.isAccessible // 通用工具方法:获取实例运行时实际类型的对应属性引用 fun <T, R> getRuntimeProperty(instance: T, property: KProperty1<T, R>): KProperty0<R> { val runtimeClazz = instance::class // 从运行时类的成员属性中匹配同名属性 @Suppress("UNCHECKED_CAST") val runtimeProperty = runtimeClazz.memberProperties .first { it.name == property.name } as KProperty1<T, R> runtimeProperty.isAccessible = true // 绑定到当前实例返回 KProperty0 return runtimeProperty.getter.apply { isAccessible = true }.bind(instance) }
使用方法非常简单,不需要强转类型:
fun main() { val foo: Foo = FooImpl(1) println("getRuntimeProperty(foo, Foo::id).hasDelegate = ${getRuntimeProperty(foo, Foo::id).hasDelegate}") }
运行输出结果为:
getRuntimeProperty(foo, Foo::id).hasDelegate = true
方案对比
你目前在用的接口声明idProp()方法的方案也可以使用,两种方案的优劣势如下:
- 上述通用反射方案:完全无侵入,不需要修改原有业务代码,适合临时调试、属性较多的场景,仅存在少量反射查找的性能开销,普通业务场景完全可以忽略
- 接口声明方法方案:无反射查找开销,性能更高,适合性能敏感、固定需要访问属性委托的场景,缺点是需要修改接口和所有实现类,有代码侵入性
内容的提问来源于stack exchange,提问作者Kolja
相关产品推荐
相关产品推荐

