2016-10-03 160 views
3

我想延長一類具有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

回答

4

可變映射是不變的,因爲寫入它們是不安全的。考慮例如以下功能:

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

非常感謝!這真的很有幫助! –

1

Scala中不可變集合能夠協變的原因是因爲如果你想添加一個元素,你實際上會創建一個全新的對象,並且可能具有不同的基礎類型。也就是說,根據需要,新地圖的基礎類型將與原始類型或超類型相同。

所以,在你的情況,如果你開始一個HashMap的實例[詮釋,兒子]並添加一個對象,它是父親,生成的地圖實際上是類型的HashMap的[詮釋,父親]。除了新地圖中的其中一個元素外,其實都是兒子對象(兒子父親的子類別)。只有你添加的那個實際上是父親。但就編譯器而言,它所知道的新地圖是所有元素都是父親

如果像我想象的那樣,如果地圖的類型比可變性更重要,那麼您應該切換到不可變類型。在這樣的集合中確實需要可變性是很少見的。

+0

謝謝,我正在嘗試使用immutable.HashMap。這是一個很好的建議。 –

相關問題