Kotlin - 通过反射获取 KProperty1 和 CallableReference 的实例

问题描述 投票:0回答:1

我正在使用一个第三方库,该库有一个函数需要

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。

kotlin reflection jvm kotlin-reflect
1个回答
0
投票

您基本上可以执行编译器遇到这样的属性引用时所做的操作 - 创建

PropertyReference1Impl
的实例。

构造函数有3个参数

  • 声明属性的类
  • 房产名称
  • getter 的 JVM 方法签名

(可选)您可以给它一个额外的 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
)。这里可能做出的其他假设并不总是正确的,但这应该适用于大多数属性。

© www.soinside.com 2019 - 2024. All rights reserved.