1. 程式人生 > >Scala11——協變、逆變、非變以及上下界

Scala11——協變、逆變、非變以及上下界

協變、逆變、非變介紹

協變和逆變主要是用來解決引數化型別的泛化問題。Scala的協變與逆變是非常有特色的,完全解決了Java中泛型的一大缺憾;舉例來說,Java中,如果有 A是 B的子類,但 Card[A] 卻不是 Card[B] 的子類;而 Scala 中,只要靈活使用協變與逆變,就可以解決此類 Java 泛型問題; 由於引數化型別的引數(引數型別)是可變的,當兩個引數化型別的引數是繼承關係(可泛化),在Java中這種情況下,那被引數化的型別是不可泛化的,然而Scala提供了三個選擇,即協變(“+”)、逆變(“-”)和非變。 下面說一下三種情況的含義,首先假設有引數化特徵Queue,那它可以有如下三種定義。 (1) trait Queue[T] {} 這是非變情況。這種情況下,當型別B是型別A的子型別,則Queue[B]與Queue[A]沒有任何從屬關係,這種情況是和Java一樣的。 (2) trait Queue[+T] {} 這是協變情況。這種情況下,當型別B是型別A的子型別,則Queue[B]也可以認為是Queue[A]的子型別,即Queue[B]可以泛化為Queue[A]。也就是被引數化型別的泛化方向與引數型別的方向是一致的,所以稱為協變。 (3) trait Queue[-T] {} 這是逆變情況。這種情況下,當型別B是型別A的子型別,則Queue[A]反過來可以認為是Queue[B]的子型別。也就是被引數化型別的泛化方向與引數型別的方向是相反的,所以稱為逆變。 總結:

C[+T]:如果A是B的子類,那麼C[A]是C[B]的子類。 C[-T]:如果A是B的子類,那麼C[B]是C[A]的子類。 C[T]: 無論A和B是什麼關係,C[A]和C[B]沒有從屬關係。

class Dog
class Xiaogou extends Dog
class Animals[+T](name: String)//協變
class Animals2[-T](name:String)//逆變
class Animals3[T](name:String)//非變
object Test{
  def main(args: Array[String]): Unit = {
    val a1:Animals[Dog]=new Animals[Xiaogou]("小狗是Dog")
    //val a2:Animals2[Dog]=new Animals2[Xiaogou]("小狗報錯啦")
    val a2:Animals2[Xiaogou]=new Animals2[Dog]("Dog是小狗")
    //type mismatch,編譯報錯
    //val a3:Animals3[Xiaogou]=new Animals3[Dog]("xiaogou又報錯了")
  }
}

上界、下界介紹

在指定泛型型別時,有時需要界定泛型型別的範圍,而不是接收任意型別。 例如,要求某個泛型型別,必須是某個類的子類,這樣在程式中就可以放心的呼叫父類的方法,程式才能正常的使用與執行。此時,就可以使用上下邊界Bounds的特性; Scala的上下邊界特性允許泛型型別是某個類的子類,或者是某個類的父類;

(1) U >: T ? super T

這是型別下界的定義,也就是U必須是型別T的父類(或本身,自己也可以認為是自己的父類)。

(2) S <: T ? extends T

這是型別上界的定義,也就是S必須是型別T的子類(或本身,自己也可以認為是自己的子類)。