[Scala] 瞭解 協變 與 逆變

首先定義一個類 A,其參數類型 T 爲協變,類中包含一個方法 func,該方法有一個類型爲 T 的參數:html

  1 class A[+T] {
  2   def func(x: T) {}
  3 }

此時在 x 處會有異常提示,covariant type T occurs in contravariant position in type T of value xweb

如今,先 假設該類定義編譯能夠經過
由於 String→AnyRef(String 是 AnyRef 的子類),參數類型 T 爲協變,因此 A[String]→A[AnyRef](A[String] 也是 A[AnyRef] 的子類)。
定義兩個對象以下:
val father: A[AnyRef] = null.asInstanceOf[A[AnyRef]] // 父類對象,包含方法 func(x: AnyRef)
val child: A[String] = null.asInstanceOf[A[String]] // 子類對象,包含方法 func(x: String)
根據 里氏替換原則,調用 father.func(Nil) 裏的 father 對象應該能夠直接替換成 child 對象,可是 child.func(Nil) 調用顯然異常。
或者定義以下一個函數,接收 A[AnyRef] 做爲參數:
  1 def otherFunc(x: A[AnyRef]): Unit = {
  2   x.func(Nil)
  3 }
同理調用 otherFunc(father) 裏的 father 對象也應該能夠直接替換成 child 對象,可是如此一來 otherFunc 中的參數要調用 func 方法就只能傳入 String 類型的參數了(由於 child 對象中的 func 方法只能接收 String 類型參數),至關於 otherFunc 的處理範圍縮小了,這是不容許的。
也就是說上面 A 類的定義違反了里氏替換原則,因此編譯器沒法經過。
圖解上述分析過程:
e8cdf7ab-5d87-4a66-8a2e-07e63f90899d[4]
反之,若是用 father 的父類對象替換,至關於 otherFunc 的處理範圍變大了,此時 otherFunc 中的參數要調用 func 方法,完成能夠傳入 AnyRef 類型的參數。
  1 val fatherFather: A[Any] = null.asInstanceOf[A[Any]] // func(x: Any)
  2 otherFunc(fatherFather)// 若是 T 爲協變,此處會提示異常

總結:

協變點(covariant position)方法返回值的位置;
逆變點(contravariant position)方法參數的位置;
由於 A 中的類型參數 T 聲明爲協變,而 T 又是 func 方法中的參數類型,屬於逆變點,因此此時編譯器會提示異常:
covariant type T occurs in contravariant position in type T of value x
 
 
  • 若是將 T 做爲 func 方法的返回值類型,即處於協變點,就能夠編譯經過。
        此時,回到以前的 otherFunc(father) 和 otherFunc(child),child 替換 father 後,child 調用 func 返回 String 類型的對象替換 AnyRef 是合理的。
  1 class B[+T] {
  2   def func(): T = {
  3     null.asInstanceOf[T]
  4   }
  5 }
  6 // 與 Function0[+A] 等價
  • 或者將類型參數 T 改成逆變,
  1 class A[-T] {
  2   def func(x: T) {}
  3 }
  4 // 與 Function1[-A, Unit] 等價
  5 
  6 val father: A[AnyRef] = null.asInstanceOf[A[AnyRef]] // func(x: AnyRef)
  7 val fatherFather: A[Any] = null.asInstanceOf[A[Any]] // func(x: Any)
  8 
  9 def otherFunc(x: A[AnyRef]): Unit = {
 10   x.func(Nil)
 11 }
 12 
 13 otherFunc(father) // success
 14 otherFunc(fatherFather)// success
 

參考網址:

 
by. Memento
相關文章
相關標籤/搜索