kotlin.time.Duration和java反射 P> >我似乎遇到了java反射API交互的一个奇怪的问题(尤其是,看起来java反射不能确定代码返回类型为 koTLIN。时间。持续时间< /代码>。考虑下面的例子: @file:JvmName("Main") package org.test.kotlin.time.duration import java.lang.reflect.Proxy import kotlin.time.Duration import kotlin.time.ExperimentalTime import kotlin.time.seconds @ExperimentalTime interface I { fun getNullableDuration(): Duration? = 1.seconds fun getRange(): IntRange = 1..3 fun getDuration(): Duration = 1.seconds } class IC: I inline fun <reified T> T.sampleProxy(): T = sampleProxy(T::class.java) fun <T> sampleProxy(c: Class<T>): T { return c.cast(Proxy.newProxyInstance(c.classLoader, arrayOf(c)) { _, method, _ -> println("[proxy] ${method.declaringClass.name}::${method.name} return type is ${method.returnType}") when (method.name) { "getNullableDuration", "getDuration" -> 1.seconds "getRange" -> 0..1 else -> TODO("method ${method.name} isn't handled yet") } }) } fun main() { val v: I = IC() println("I::getNullableDuration() return type is ${v::getNullableDuration.returnType}") v.sampleProxy().getNullableDuration() println("I::getRange() return type is ${v::getRange.returnType}") v.sampleProxy().getRange() println("I::getDuration() return type is ${v::getDuration.returnType}") v.sampleProxy().getDuration() }
可以看到,在kotlin.time.Duration和java反射 P> >我似乎遇到了java反射API交互的一个奇怪的问题(尤其是,看起来java反射不能确定代码返回类型为 koTLIN。时间。持续时间< /代码>。考虑下面的例子: @file:JvmName("Main") package org.test.kotlin.time.duration import java.lang.reflect.Proxy import kotlin.time.Duration import kotlin.time.ExperimentalTime import kotlin.time.seconds @ExperimentalTime interface I { fun getNullableDuration(): Duration? = 1.seconds fun getRange(): IntRange = 1..3 fun getDuration(): Duration = 1.seconds } class IC: I inline fun <reified T> T.sampleProxy(): T = sampleProxy(T::class.java) fun <T> sampleProxy(c: Class<T>): T { return c.cast(Proxy.newProxyInstance(c.classLoader, arrayOf(c)) { _, method, _ -> println("[proxy] ${method.declaringClass.name}::${method.name} return type is ${method.returnType}") when (method.name) { "getNullableDuration", "getDuration" -> 1.seconds "getRange" -> 0..1 else -> TODO("method ${method.name} isn't handled yet") } }) } fun main() { val v: I = IC() println("I::getNullableDuration() return type is ${v::getNullableDuration.returnType}") v.sampleProxy().getNullableDuration() println("I::getRange() return type is ${v::getRange.returnType}") v.sampleProxy().getRange() println("I::getDuration() return type is ${v::getDuration.returnType}") v.sampleProxy().getDuration() },java,kotlin,reflection,Java,Kotlin,Reflection,可以看到,在sampleProxy中,getNullableDuration()和getRange()的返回类型被正确地确定(kotlin.time.Duration?预期变成kotlin.time.Duration),而getDuration()突然变成一个返回double的方法,从该方法返回时从kotlin.time.Duration到double的转换失败,出现异常 问题的原因是什么?我如何解决它 我的环境: OpenJDK 11.0.6+10 科特林1.3.71 Linux/x86-6
sampleProxy
中,getNullableDuration()
和getRange()
的返回类型被正确地确定(kotlin.time.Duration?
预期变成kotlin.time.Duration
),而getDuration()
突然变成一个返回double
的方法,从该方法返回时从kotlin.time.Duration到double的转换失败,出现异常
问题的原因是什么?我如何解决它
我的环境:
- OpenJDK 11.0.6+10
- 科特林1.3.71
- Linux/x86-64
类
这意味着:
- Kotlin编译器将尽可能使用
double
- 如果不是包装器类型,则将改为使用
本节:
在生成的代码中,Kotlin编译器为每个内联类保留一个包装器。内联类实例在运行时可以表示为包装器或底层类型
Kotlin编译器更喜欢使用底层类型而不是包装器来生成最高效和优化的代码。但是,有时有必要保留包装器。根据经验,内联类作为另一种类型使用时会被装箱。
在提供的示例中:
getNullableDuration
返回Duration?
,这意味着装箱值(参见Kotlin文档部分中的示例)
getDuration
只返回Duration
,允许编译器使用底层类型double
而不是包装器
- 行
“getNullableDuration”,“getDuration”->1.seconds
返回一个包装器而不是底层类型,因为返回一个对象,不允许编译器使用底层类型
这就是抛出ClassCastException
的原因。编译的getDuration
方法返回一个double
,但代理将始终返回Duration
以下是IC
反编译为Java时的样子:
公共最终类IC实现I{
@可空
公共持续时间getNullableDuration(){
返回I.DefaultImpls.getNullableDuration(此);
}
@NotNull
公共内部网getRange(){
返回I.DefaultImpls.getRange(此);
}
//这里是双人房
公共双getDuration(){
返回I.DefaultImpls.getDuration(此);
}
}
权变措施
最简单的方法是使用可为null的类型
另一种方法是为getDuration
方法返回double
:
when(method.name){
“getNullableDuration”->1.5秒
“getDuration”->1.0
“getRange”->0..1
else->TODO(“尚未处理方法${method.name}”)
}
请注意,时间api和inline
类都是实验性的
I::getNullableDuration() return type is kotlin.time.Duration?
[proxy] org.test.kotlin.time.duration.I::getNullableDuration return type is class kotlin.time.Duration
I::getRange() return type is kotlin.ranges.IntRange
[proxy] org.test.kotlin.time.duration.I::getRange return type is class kotlin.ranges.IntRange
I::getDuration() return type is kotlin.time.Duration
[proxy] org.test.kotlin.time.duration.I::getDuration return type is double
Exception in thread "main" java.lang.ClassCastException: class kotlin.time.Duration cannot be cast to class java.lang.Double (kotlin.time.Duration is in unnamed module of loader 'app'; java.lang.Double is in module java.base of loader 'bootstrap')
at com.sun.proxy.$Proxy2.getDuration(Unknown Source)
at org.test.kotlin.time.duration.Main.main(main.kt:38)
at org.test.kotlin.time.duration.Main.main(main.kt)
Process finished with exit code 1