【问题标题】:Why mutable lists of different types behave as immutable when put inside a mutable list为什么将不同类型的可变列表放在可变列表中时表现为不可变
【发布时间】:2021-10-28 01:49:23
【问题描述】:

我们知道,在 Kotlin 中可以创建不同类型的可变列表的可变列表,例如:

val list = mutableListOf(
            MutableList<Int>(3) { 0 }, 
            MutableList<String>(2) { "aaa" }
        )  

并且可以(如预期)更改主列表,例如:

list.add(MutableList<Double>(4) { 0.0 }

经过这个操作,println(list)的结果如下:

[[0, 0, 0], [aaa, aaa], [0.0, 0.0, 0.0, 0.0]]

但不可能更改子列表,
例如操作:

list[0][1] = 2
list[1].add("bbb")

产生错误信息:
整数文字不符合预期的类型 Nothing

类型不匹配:推断的类型是字符串,但没有任何预期
分别...

我的问题是
为什么这些嵌套子列表 - 声明为可变 - 表现得像不可变???
有没有改变这些嵌套子列表的方法???


顺便说一句...
我已经检查过,声明为可变的嵌套子列表在它们都是相同类型时表现得非常正常。
在这种情况下,可以 - 正如预期的那样 - 更改那些嵌套的子列表......

【问题讨论】:

    标签: kotlin mutability mutablelist


    【解决方案1】:

    由于 MutableList 是类型化的,因​​此您必须在向其添加项目之前让编译器知道子列表的确切类型。 以下代码:

    (list[0] as MutableList<Int>)[1] = 2
    (list[1] as MutableList<String>).add("bbb")
    

    效果很好。但是,当您尝试强制转换为错误的 MutableList 类型时,仍然可能在运行时遇到问题,这就是报告警告(“Unchecked cast”)的原因。

    当所有子列表属于同一类型时,例如Int 顶级 list 类型被编译器明确推断为 MutableList&lt;MutableList&lt;Int&gt;&gt;,因此您不必明确告诉编译器子列表的类型,因为它已经知道它们。但是当子列表的类型不同时,编译器会将list 的类型推断为MutableList&lt;MutableList&lt;Any&gt;&gt;,并限制您仅调用MutableList 的非类型化方法(例如clear())而不进行强制转换。

    【讨论】:

    • 谢谢@Nikolai!它确实有效,但对我来说你必须强制转换似乎很奇怪 - 在所有类型的子列表之前都明确给出了
    • 你必须强制转换它们的原因是编译器不知道它们是什么。为了让编译器随时知道父列表的每个索引中的类型,它必须足够先进,能够在运行时运行应用程序行为的整个模拟。当编译器变得如此先进时,他们将为我们编写所有代码。
    • 我看到关于泛型的 Kotlin 文档主题有助于理解这个问题 - 请参阅:kotlinlang.org/docs/generics.html
    • @Tenfour04 - 现在我认为 - 严格来说 - 编译器(在源代码级别)知道子列表的类型是什么,正如他们明确指出的那样......但是 - 因为'类型擦除' - 运行时环境(例如 JVM)不知道它们,所以我们必须使用显式强制转换
    • 在所有可能的逻辑行中,在代码中的这个位置,值不可能是浮点数以外的任何东西。有很多事情可以改变它。也许它被传递给不同的函数,也许这些函数的调用顺序是不同的。这些函数可以以无限的顺序组合多次调用。数组可以从类中传递出去,或者我们可以在内存中使用一个新数组来替换最初存储在该属性中的数组,等等。想象编译器总是可以知道每个索引中的内容是没有意义的。跨度>
    猜你喜欢
    • 1970-01-01
    • 2015-12-16
    • 2020-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-05
    • 2020-02-17
    • 2015-12-14
    相关资源
    最近更新 更多