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
。
所以我的两个问题是:
我怎样才能使作品使用mutable.HashMap?
scala的作者为什么要这样设计HashMap?
可变映射是不变的,因为写入它们是不安全的。考虑例如以下功能:
def f(o: C1) {
o.m(42) = new Father
}
这种方法是非常好的类型。但是,如果您通过C2
的实例作为o
的值传递,则会因为Map[Int, Son]
不允许包含Father
对象而中断。因此你的C2
的定义是不正确的。
另外 - 我推荐阅读Daniel Spiewak对方差的有用答案:http://stackoverflow.com/a/674090/409976。 –
非常感谢!这真的很有帮助! –
Scala中不可变集合能够协变的原因是因为如果你想添加一个元素,你实际上会创建一个全新的对象,并且可能具有不同的基础类型。也就是说,根据需要,新地图的基础类型将与原始类型或超类型相同。
所以,在你的情况,如果你开始一个HashMap的实例[诠释,儿子]并添加一个对象,它是父亲,生成的地图实际上是类型的HashMap的[诠释,父亲]。除了新地图中的其中一个元素外,其实都是儿子对象(儿子是父亲的子类别)。只有你添加的那个实际上是父亲。但就编译器而言,它所知道的新地图是所有元素都是父亲。
如果像我想象的那样,如果地图的类型比可变性更重要,那么您应该切换到不可变类型。在这样的集合中确实需要可变性是很少见的。
谢谢,我正在尝试使用immutable.HashMap。这是一个很好的建议。 –
我想你会发现所有的Scala集合都是如此:可变性影响类型的变化。可变集合上可用的操作使协方差不安全。 – jwvh