【问题标题】:Why can't Kotlin infer the type for this HashMap with a nullable value type?为什么 Kotlin 不能用可空值类型推断此 HashMap 的类型?
【发布时间】:2021-04-13 21:54:49
【问题描述】:

当我运行这段代码时,我得到一个Type mismatch: inferred type is kotlin.collections.HashMap<String, Int> /* = java.util.HashMap<String, Int> */ but kotlin.collections.HashMap<String, Any?> /* = java.util.HashMap<String, Any?> */ was expected 错误

data class Record(
        var blah: HashMap<String, Any?>
)
    
fun test2() {
        val data2 = hashMapOf("key" to 10000)
        val a = Record(data2)
        println(a.blah)
}

在 Kotlin Playgrounds 上运行上述代码:https://pl.kotl.in/vL2n_Qrwo

只有当数据类中的类型是 HashMap 时我才会得到这个错误,当它只是一个 Map (https://pl.kotl.in/f1V3Eeyj-) 时我不会得到它。为什么是这样?通过明确指定 Any? (任何带有问号,以指定它可以为空)在 hashMapOf 类型中所以hashMapOf&lt;String, Any?&gt;("key" to 10000)

最奇怪的是,如果我不创建一个新变量data2 来保存哈希图,它不会返回任何错误!

data class Record(
        var blah: HashMap<String, Any?>
)
    
fun test1() {
    val a = Record(hashMapOf("key" to 10000))
    println(a.blah)
}

在 Kotlin Playgrounds 上运行这个:https://pl.kotl.in/K12q1Bd7B

如您所见,没有错误。这对我来说毫无意义。

【问题讨论】:

    标签: java kotlin hashmap


    【解决方案1】:

    推理引擎一次只分析一个语句。

    问题是关于fun &lt;K, V&gt; hashMapOf(): HashMap&lt;K, V&gt; 的两个类型参数的推断,即KV 的类型。

    在第一个例子val data2 = hashMapOf("key" to 10000)中,对于hashMapOf()的结果没有给出要求,所以引擎使用参数来确定这两种类型。 "key" to 10000 是一对StringInt,所以结果是KStringVInt

    在第二个示例val a = Record(hashMapOf("key" to 10000)) 中,Record 构造函数要求hashMapOf() 返回的对象的类型必须是一个赋值对象与HashMap&lt;String, Any?&gt; 兼容。因此推理引擎使用K 作为StringV 作为Any,并且编译接受这一点,因为"key"String 兼容并且10000Any? 兼容。

    【讨论】:

    • blahMap&lt;String, Any?&gt; 时,您能否解释为什么OP 的第一个示例中的推断不同?
    • 啊,安德烈亚斯回答我的第二个(也是最令人困惑的)问题非常有意义,谢谢。这不是 kotlin 插件的缺点吗?不是应该足够聪明才能弄清楚吗?无论如何,我赞同@HenryTwist 所说的话,对第一个问题有什么想法吗?我知道这违反了 SO 协议,我应该把问题分开。我怀疑答案与 Kotlin mapOf 返回任何 Map 类型有关,而 hashMapOf 专门返回 Java HashMap
    • 与什么相比的缺点? Java 在这种推理方面并不聪明。
    • @LouisWasserman 好吧,这在 Java 上根本不是问题,因为我必须明确指定 data2 的 HashMap K、V 类型。这只是我认为 Kotlin 可以解决的问题
    • 确定类型时不会提前读取到下一行。在我看来,也不应该。任何代码行的含义都应该是决定性的,没有任何后续代码会影响该含义。
    【解决方案2】:

    为了回答您问题的第一部分,HashMap(或 Kotlin MutableMap)是使用泛型参数 KV 定义的,而只读 Map 是使用泛型参数 @ 定义的987654327@和out V

    MapV 可以具有 out 修饰符,因为 Map 是只读的,因此该值仅存在于其函数签名的“输出”部分中——在其他换句话说,V 类型的值仅从Map 返回,从不提供给Map。见Declaration-site Variance

    正如@Andreas 的回答所指出的,hashMapOf("key" to 10000) 的推断类型是HashMap&lt;String, Int&gt;

    当你定义时:

    var blah: HashMap<String, Any?>
    

    编译器说:不,我不能将HashMap&lt;String, Int&gt; 分配给HashMap&lt;String, Any?&gt; 类型的变量,因为稍后有人可以在此处有效地放入Int 以外的其他内容,这显然是不正确的.

    您可以通过更改赋值表达式以显式指定类型来告诉编译器“不,如果 Int 以外的其他内容进入此处,则可以”,然后一切都很好:

    val data2 = hashMapOf<String, Any?>("key" to 10000)
    

    相应地,当您将blah 定义为只读Map 而不是可变HashMap(或Kotlin MutableMap)时:

    var blah: Map<String, Any?>
    

    编译器可以将HashMap&lt;String, Int&gt; 分配给它,因为现在Any? 具有out 方差,即blah 只能返回Any?,而不接受它们。

    【讨论】:

    • 啊,这解释了,谢谢@Raman! HashMap 是可变的(因为它是 Java),而 Kotlin Map 不是。我刚刚测试了将 blah 更改为 MutableMap&lt;String, Any?&gt; 并返回错误
    • @georgiecasey 你明白了。我在答案中添加了MutableMap
    猜你喜欢
    • 1970-01-01
    • 2019-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多