我正在try 将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. 为什么隐式转换只在类型不可为空时才起作用?

  2. 当类型可为空时如何进行隐式转换?

推荐答案

假设这是Kotlin/JVM...

在深入研究了callBy在JVM上是如何实现的之后,我发现它调用了Constructor.newInstance.这方面的文档中写道:

各个参数被自动展开以匹配基本形式参数,并且基本参数和引用参数都根据需要接受方法调用转换.

当您使用Kotlin中不可为空的Double作为参数类型时,它将被转换为JVM世界中的原始类型double.但是,如果使用可为空的类型Double?,则会将其转换为引用类型包装java.lang.Double.这是因为基元类型double不能为空.IntInt?也发生了类似的事情.

newInstance个文档提到的"方法调用转换"(我认为是指the list of conversions allowed in an invocation context)不包括从intjava.lang.Double的转换.毕竟,这段Java代码不能编译:

public static void foo(Double d) {}

// ...

int x = 1;
foo(x)l

但如果foo取了原始的double,它就会被编译.

至于您的第二个问题,我想不出比手动判断每个参数和参数类型,并为每种情况显式执行转换更好的方法了.

如果可以更改Person,我建议添加一个接受不可为空的Double的辅助构造函数,并更改调用代码以 Select 合适的构造函数.

或者,如果您的really不喜欢 Select 构造函数,请将现有的构造函数更改为接受Number?,并在使用它之前使用toXXX方法将其转换为适当的数值类型.

Kotlin相关问答推荐

Gradle Jooq配置自定义生成器

在Kotlin中求n个ClosedRange实例相交的最常用方法是什么?

可以从背景图像中点击图标吗?

Scala与Kotlin中的迭代

在 Kotlin 中,为什么我们要在闭包中捕获值

在 Kotlin 中将两个字节转换为 UIn16

使用 kotlin 流删除 map 中具有某些相似性的值

内容更改后的 var 重新计算

Kotlin 中二叉树的深度

将子元素放在一个列表中

如何将超过 2 个 api 调用的结果与 Coroutines Flow 结合起来?

零安全的好处

Android 上的 Kotlin:将map到list

使用 Kotlin 创建自定义 Dagger 2 范围

这是 Kotlin 中的错误还是我遗漏了什么?

在 Kotlin 中创建非绑定服务

我应该使用Kotlin数据类作为JPA实体吗?

Kotlin扩展函数与成员函数?

在Kotlin中将列表转换为对的惯用方法

可见性不适用于 Kotlin