Kotlin:从Int到可空Double的隐式转换在反射中失败

7ajki6be  于 2023-06-24  发布在  Kotlin
关注(0)|答案(1)|浏览(128)

我尝试将Map<String, Any>转换为数据类示例:

data class Person(
    val name: String,
    val age: Int,
    val weight: Double?,
)

fun test() {
    val map = mapOf(
        "name" to "steven",
        "age" to 30,
        "weight" to 60,
    )
    
    val ctor = Person::class.constructors.first();
    val params = ctor.parameters.associateWith {
        map[it.name]
    }
        
    val instance = ctor.callBy(params)
    println(instance)
}

上面的代码抛出java.lang.IllegalArgumentException: argument type mismatch,因为60作为Int传递给weight,而Kotlin不支持隐式转换。
然后将weight的类型更改为不可空的Double

data class Person(
    val name: String,
    val age: Int,
    val weight: Double, // !
)

这样就行了,甚至60F也行。
我的问题是:
1.为什么隐式转换只在类型不可为空时起作用?
1.如何在类型可为空时进行隐式转换?

q3qa4bjr

q3qa4bjr1#

假设这是Kotlin/JVM。
在深入研究了callBy如何在JVM上实现之后,我发现它最终会调用Constructor.newInstance。相关文档显示:
单个参数会自动展开以匹配原始形式参数,并且原始参数和引用参数都需要进行方法调用转换。
当您在Kotlin中使用不可空的Double作为参数类型时,它会被转换为JVM世界中的原始类型double。但是,如果使用可空类型Double?,则会将其转换为引用类型 Package 器java.lang.Double。这是因为基本类型double不能为null。类似的情况发生在IntInt?
newInstance文档中提到的“方法调用转换”(我认为是指调用上下文中允许的转换列表)不包括从intjava.lang.Double的转换。毕竟,这段Java代码不会编译:

public static void foo(Double d) {}

// ...

int x = 1;
foo(x)l

但如果foo采用了double原语,它将编译。
至于你的第二个问题,我想不出比手动检查每个参数和参数类型,并为每种情况显式执行转换更好的方法。
如果可以更改Person,我建议添加一个接受不可空的Double的二级构造函数,并更改调用代码以选择适当的构造函数。
或者,如果你真的不喜欢选择构造函数,可以将现有的构造函数改为Number?,并在使用它之前使用toXXX方法将其转换为适当的数值类型。

相关问题