scala.collection.mutable中的HashMap是不变的但是不可变.HashMap是协变的,为什么?

时间:2016-10-03 15:36:45

标签: scala

我想扩展一个类为mutable.HashMap []的类,如下所示:

class Father
class Son extends Father    

class C1{
  val m = new mutable.HashMap[Int, Father]() 
}

class C2 extends C1{
  override val m = new mutable.HashMap[Int, Son]()
}

并收到错误:

  

错误:(19,16)覆盖类型C1中的值m   scala.collection.mutable.HashMap [中等,ScalaByExample.Father];价值m   具有不兼容的类型覆盖val m = new mutable.HashMapInt,   子

我发现immutable.HashMap是协变的,但mutable.HashMap是不变的。如果将mutable.HashMap替换为immutable.HashMap,则可以使用。

所以我的两个问题是:

  1. 如何使用mutable.HashMap?

  2. 使其工作
  3. 为什么scala的作者会像这样设计HashMap?

2 个答案:

答案 0 :(得分:4)

可变地图是不变的,因为写入它们不会是安全的。例如考虑以下函数:

def f(o: C1) {
    o.m(42) = new Father
}

这种方法非常好。但是,如果您传递C2的实例作为o的值,则会因为Map[Int, Son]不允许包含Father个对象而中断。因此,您对C2的定义是错误的。

答案 1 :(得分:1)

Scala中的不可变集合能够协变的原因是因为如果要添加元素,您实际上将创建一个具有(可能)不同底层类型的全新对象。根据需要,新地图的基础类型将与原始地图的类型相同或超类型。

因此,在您的情况下,如果您从 HashMap [Int,Son] 的实例开始并添加一个的对象,则生成的地图实际上是属于 HashMap [Int,Father] 。新地图中除了一个元素之外的所有元素实际上都是 Son 对象( Son 的子类)。只有您添加的那个实际上才是。但就编译器而言,它所知道的新地图是所有元素都是的类型。

如果像我想象的那样,你的地图类型对你来说比可变性更重要,那么你应该切换到不可变类型。很难在这样的集合中真正需要可变性。