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

问题描述:

我想延长一类具有mutable.HashMap的[]这样的VAL:scala.collection.mutable中的HashMap是不变的,但不可变.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]() 
} 

,并得到一个错误:

Error:(19, 16) overriding value m in class C1 of type scala.collection.mutable.HashMap[Int,ScalaByExample.Father]; value m has incompatible type override val m = new mutable.HashMapInt, Son

我发现immutable.HashMap是协变,但mutable.HashMap是不变的。它适用于如果将mutable.HashMap替换为immutable.HashMap

所以我的两个问题是:

  1. 我怎样才能使作品使用mutable.HashMap?

  2. scala的作者为什么要这样设计HashMap?

+0

我想你会发现所有的Scala集合都是如此:可变性影响类型的变化。可变集合上可用的操作使协方差不安全。 – jwvh

可变映射是不变的,因为写入它们是不安全的。考虑例如以下功能:

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

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

+0

另外 - 我推荐阅读Daniel Spiewak对方差的有用答案:http://stackoverflow.com/a/674090/409976。 –

+0

非常感谢!这真的很有帮助! –

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

所以,在你的情况,如果你开始一个HashMap的实例[诠释,儿子]并添加一个对象,它是父亲,生成的地图实际上是类型的HashMap的[诠释,父亲]。除了新地图中的其中一个元素外,其实都是儿子对象(儿子父亲的子类别)。只有你添加的那个实际上是父亲。但就编译器而言,它所知道的新地图是所有元素都是父亲

如果像我想象的那样,如果地图的类型比可变性更重要,那么您应该切换到不可变类型。在这样的集合中确实需要可变性是很少见的。

+0

谢谢,我正在尝试使用immutable.HashMap。这是一个很好的建议。 –