这段代码有效:
@ 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
转换为不可变是对底层集合的可变性的"撒谎",但在某些情况下,程序员知道得更多 - 例如,当从使用可变列表构建结果但返回不可变值的函数返回集合时。
默认Map
在Predef
中定义为
type Map[A, +B] = collection.immutable.Map[A, B]
所以它是显式immutable
的,mutable.Map
不是它的子类。
与此相反,默认Seq
直接在scala
定义为
type Seq[+A] = scala.collection.Seq[A]
所以它是mutable.Seq
和immutable.Seq
的超型.这就是为什么您的第一次asInstanceOf
不会失败的原因:每个mutable.Seq
也是一个collection.Seq
。
正如这里所解释的,不指定Seq
必须是可变的还是不可变的,这与对数组和变量的支持有关。
在 2.13 中,默认Seq
将变为immutable
,并引入一个新的类型ImmutableArray
来处理varargs
。(感谢@SethTisue指出)
主要问题是:
如果scala.collection.mutable.Map
是scala.collection.immutable.Map
的一个子类,那么前者也是后者。也就是说,可变Map
也是不可变的。这有意义吗?
为了说明这一点,您可以将可变Map
的实例传递给期望不可变Map
的函数或构造函数。唉,这两种类型有不同的语义:比如说,如果你在不可变版本中添加一个元素,你会得到一个新的不可变Map
实例返回;但是,如果您将元素添加到可变版本,它会更改该实例的内容,因此,它将产生副作用。
因此,如果你想编写一个纯粹的、引用透明的(RT) 函数(即没有副作用的函数),它采用不可变的Map
参数,你就无法实现你的目标——任何人都可以通过向它传递一个可变的Map
实例来搞砸它。然后,这将改变代码的含义,并可能导致各种问题。
在函数式编程中,不变性很重要,RT也是如此。通过确保两者不会混淆,需要不可变Map
的程序可以保证它们会得到它们。
(当然,如果您明确希望编写接受其中任何一个的代码,则可以请求其共同scala.collection.Map
特征的实例。