我正在使用一个第三方库,该库有一个函数需要
KProperty1<T, V>
类型的参数,并在内部将其转换为 CallableReference
的实例。我需要通过反射获取对类成员的引用,该类成员是 KProperty1
和 CallableReference
的实例。
第三方库中的函数是这样的:
fun <T : Any, V> thirdPartyFunction(property: KProperty1<T, V>) {
val callableReference = (property as CallableReference)
}
当我使用通过
::
运算符获得的成员引用调用函数时,一切正常:
class ExampleClass(
var exampleProperty: String,
)
// this works
thirdPartyFunction(ExampleClass::exampleProperty)
当我使用通过反射获得的值调用该函数时,我得到一个
ClassCastException
:
val reflectedProperty = ExampleClass::class.memberProperties
.find { it.name == "exampleProperty" } as KProperty1<ExampleClass, String>
// class kotlin.reflect.jvm.internal.KMutableProperty1Impl cannot be cast to class kotlin.jvm.internal.CallableReference
thirdPartyFunction(reflectedProperty)
我在 JVM 上使用 Kotlin 1.9.21。
您基本上可以执行编译器遇到这样的属性引用时所做的操作 - 创建
PropertyReference1Impl
的实例。
构造函数有3个参数
(可选)您可以给它一个额外的 flags 参数,指示它是否在 Java 世界中是合成的,和/或是否在顶层(而不是在类中)声明。如果它是扩展属性,您还可以给它一个接收器。
让我们考虑以下简单情况:
class Foo {
val x = 1
}
对于
Foo::x
,您可以写:
PropertyReference1Impl(Foo::class, "x", "getX()I")
您可以编写这样的方法来更普遍地处理这个问题:
inline fun <reified T, R> KProperty1<T, *>.asCallableReference(): KProperty1<T, R> {
val getter = javaGetter ?: throw Exception("Must have a JVM getter for this to work!")
return PropertyReference1Impl(
T::class, name,
getter.name + MethodType.methodType(getter.returnType)
.toMethodDescriptorString()
) as KProperty1<T, R>
}
这假设接收器来自某些
memberProperties
的 KClass
集合,并且它不是扩展属性,并且它有一个不带参数的 Java getter(不是 @JvmField
)。这里可能做出的其他假设并不总是正确的,但这应该适用于大多数属性。