在函数
f
中,我想对两个值a.x
和b.x
求和:
class C<T>(var x: T)
inline fun <reified T> f(a: C<T>, b: C<T>): T? {
val c = T::class
val members = c.members.groupBy { it.name }
for (m in members["plus"]!!) {
if (m.parameters[1].type == Int::class.createType()) {
return m.call(a.x, b.x) as T?
}
}
return null
}
fun main(args: Array<String>) {
val a = C(1)
val b = C(2)
println(f(a, b))
}
错误:
Exception in thread "main" kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: @kotlin.internal.IntrinsicConstEvaluation public final operator fun plus(other: kotlin.Int): kotlin.Int defined in kotlin.Int[DeserializedSimpleFunctionDescriptor@2f4205be] (member = null)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:98)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:64)
at kotlin.SafePublicationLazyImpl.getValue(LazyJVM.kt:107)
at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:64)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108)
at MainKt.main(Main.kt:66)
目前这是不可能的。
Int::plus
实际上并不存在,因为没有对应的 JVM 方法。您无法致电 Int::plus
。
当像
someInt + anotherInt
这样的东西被编译时,它不会被转换为对 Int::plus
的调用。它只是直接翻译成 iadd
JVM 指令。
最终,Kotlin 反射会尝试查找相应的 JVM 方法,因此当找不到时,它会抛出
KotlinReflectionInternalError
。请参阅KFunctionImpl
中的实现
val member: Member? = when (val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)) {
// ...
is KotlinFunction -> {
// ...
container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc) as Member?
}
// ...
}
when (member) {
is Constructor<*> -> // ...
is Method -> // ...
else -> throw KotlinReflectionInternalError("Could not compute caller for function: $descriptor (member = $member)")
}.createValueClassAwareCallerIfNeeded(descriptor)
您可以看到,Kotlin 函数首先映射到其 JVM 签名,然后使用
container.findMethodBySignature
在 Int
中查找具有该签名的 JVM 方法。这样的方法不存在,所以container.findMethodBySignature
返回null,并且在第二个when
中抛出异常。
已经有错误报告报告了此问题。例如,请参阅KT-13077(及其相关问题)。 KT-13077 (
Int::toByte
) does 中提到的具体情况有点像 JVM 对应项 (java.lang.Integer::byteValue
),而且它们还使用可调用引用表达式,这使得异常消息与您的有点不同.
无论如何,这是 Kotlin 反射还无法做到的事情。