为什么不可变。映射扩展不可变。地图?



这段代码有效:

@ mutable.Seq(1, 2).asInstanceOf[Seq[Int]]
res1: Seq[Int] = ArrayBuffer(1, 2)

但这不会:

@ mutable.Map(1 -> 2).asInstanceOf[Map[Int, Int]]
java.lang.ClassCastException: scala.collection.mutable.HashMap cannot be cast
to scala.collection.immutable.Map
ammonite.$sess.cmd1$.<init>(cmd1.sc:1)
ammonite.$sess.cmd1$.<clinit>(cmd1.sc)

为什么mutable.Seq可以被视为不可变的,但不能被视为mutable.Map?我知道将可变Seq转换为不可变是对底层集合的可变性的"撒谎",但在某些情况下,程序员知道得更多 - 例如,当从使用可变列表构建结果但返回不可变值的函数返回集合时。

默认MapPredef中定义为

type Map[A, +B] = collection.immutable.Map[A, B]

所以它是显式immutable的,mutable.Map不是它的子类。

与此相反,默认Seq直接在scala定义为

type Seq[+A] = scala.collection.Seq[A] 

所以它是mutable.Seqimmutable.Seq的超型.这就是为什么您的第一次asInstanceOf不会失败的原因:每个mutable.Seq也是一个collection.Seq

正如这里所解释的,不指定Seq必须是可变的还是不可变的,这与对数组和变量的支持有关。

在 2.13 中,默认Seq将变为immutable,并引入一个新的类型ImmutableArray来处理varargs。(感谢@SethTisue指出)

主要问题是:

如果scala.collection.mutable.Mapscala.collection.immutable.Map的一个子类,那么前者也是后者。也就是说,可变Map也是不可变的。这有意义吗?

为了说明这一点,您可以将可变Map的实例传递给期望不可变Map的函数或构造函数。唉,这两种类型有不同的语义:比如说,如果你在不可变版本中添加一个元素,你会得到一个新的不可变Map实例返回;但是,如果您将元素添加到可变版本,它会更改该实例的内容,因此,它将产生副作用

因此,如果你想编写一个纯粹的、引用透明的(RT) 函数(即没有副作用的函数),它采用不可变Map参数,你就无法实现你的目标——任何人都可以通过向它传递一个可变Map实例来搞砸它。然后,这将改变代码的含义,并可能导致各种问题。

函数式编程中,不变性很重要,RT也是如此。通过确保两者不会混淆,需要不可变Map的程序可以保证它们会得到它们。

(当然,如果您明确希望编写接受其中任何一个的代码,则可以请求其共同scala.collection.Map特征的实例。