关于scala:为什么不可变。Map扩展了immutable.Map?

Why doesn't mutable.Map extend immutable.Map?

此代码有效:

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

但这不是:

1
2
3
4
5
@ 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转换为不可变的Seq是"说谎"底层集合的可变性,但是在某些情况下,程序员会更好地了解它。从使用可变列表构建结果但返回不可变值的函数返回集合时。


默认MapPredef中定义为

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

因此它明确地是immutable,而mutable.Map不是它的子类。

与此相反,默认的Seqscala中直接定义为

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

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

如此处所述,不指定Seq必须是可变的还是不可变的决定与对数组和varargs的支持有关。

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


主要问题是:

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

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

因此,如果您想编写一个带有不变的Map参数的纯的,参照透明(RT)的函数(即没有副作用的函数),则您将无法实现目标,任何人都可以通过传递一个可变的Map实例来解决这个问题。然后,这将更改代码的含义,并可能导致各种形式的问题。

在函数式编程中,不变性和RT一样重要。通过确保两者不会混淆,需要不变的Map的程序可以保证它们将得到它们。

(当然,如果您明确想要编写将接受其中任何一个的代码,则可以请求其常见scala.collection.Map特性的一个实例。)