2013-01-11 59 views
5

我已代碼:誤差定義協變和逆變類型

class A { 
    override def toString = "object class A" 
} 

class B extends A { 
    override def toString = "object class B" 
} 

class Cell[+T](init: T) { 
    private[this] var current: T = init 
    def get: T = current 
    def set(x: T) { current = x } 
} 

val cB = new Cell[B](new B) 
println(cB.get) 
val cA: Cell[A] = cB 
println(cA.get) 

,但我有錯誤行:def set(x: T) { current = x }

error: covariant type T occurs in contravariant position in type T of value x def set(x: T) { current = x }

解釋,請

+1

除了有見地的答案,爲什麼你需要明確私有引用和getter/setter方法? Scala爲你做這件事:'class Cell [+ T](val t:T){...}' –

回答

5

一類反變位置(其中包括)允許您將該類型的實例傳遞給方法的任何位置。所以所有的方法參數類型都處於逆變位置。既然你將T聲明爲協變(+T),編譯器將不會允許這樣做。你唯一的選擇是:

  • 化妝T不變
  • 修改的設置方法,使其返回Cell一個新實例,並Cell從而成爲不可改變的。
  • 取出一套方法,也使Cell不變

如果編譯器允許你有一套方法,你實現它,這將使類型系統不安全,因爲它允許你寫:

val cs:Cell[String] = new Cell("") 
val ca:Cell[Any] = cs 
ca.set(5) 
val s:String = cs.get //boom 
+0

應該是'ca.set(5)'而不是'cs.set(5)' –

+0

謝謝,改變了它 –

4

簡短的回答是,你不能有一個協變的可變容器。假設這個編譯,然後在你的榜樣,最後兩行可以閱讀:

val cA: Call[A] = cB 
cA.set(new A) 

第一線將被允許的,因爲一個Cell[B]一個Cell[A]由於使用的+T。然後第二行也被允許 - 因爲你當然可以設置一個Cell[A]來保存A

但是現在,cB.get返回A的實例,而不是B!這沒有任何意義,並且沒有辦法解決這個矛盾,同時仍然允許Cell的類型參數中的協變。


對於更長,更正式的/全面的答案,看到這個問題:Scala covariance/contravariance