測試源代碼git
歡迎你們關注: scala工具庫 ,裏面包含各類庫的測試用例和使用說明文檔github
B是A的子類,A是B的父類。segmentfault
當咱們定義一個協變類型List[A+]時,List[Child]能夠是List[Parent]的子類型。工具
當咱們定義一個逆變類型List[-A]時,List[Child]能夠是List[Parent]的父類型。oop
##Scala的協變測試
看下面的例子:ui
class Animal {} class Bird extends Animal {} class Animal {} class Bird extends Animal {} //協變 class Covariant[T](t:T){} val cov = new Covariant[Bird](new Bird) val cov2:Covariant[Animal] = cov
cov不能賦值給cov2,由於Covariant定義成不變類型。scala
稍微改一下:code
class Animal {} class Bird extends Animal {} class Animal {} class Bird extends Animal {} //協變 class Covariant[+T](t:T){} val cov = new Covariant[Bird](new Bird) val cov2:Covariant[Animal] = cov
由於Covariant定義成協變類型的,因此Covariant[Bird]是Covariant[Animal]的子類型,因此它能夠被賦值給c2。ci
##Scala的逆變
將上面的例子改一下:
class Animal {} class Bird extends Animal {} class Contravariant[-T](t: T) { } val c: Contravariant[Animal] = new Contravariant[Animal](new Animal) val c2: Contravariant[Bird] = c
這裏Contravariant[-T]定義成逆變類型,因此Contravariant[Animal]被看做Contravariant[Animal]的子類型,故c能夠被賦值給c2。
##下界lower bounds
若是協變類包含帶類型參數的方法時:
class Animal {} class Bird extends Animal {} class Consumer[+T](t: T) { def use(t: T) = {} }
編譯會出錯。出錯信息爲 "Covariant type T occurs in contravariant position in type T of value t"。 可是若是返回結果爲類型參數則沒有問題。
class Animal {} class Bird extends Animal {} class Consumer[+T](t: T) { def get(): T = {new T} }
爲了在方法的參數中使用類型參數,你須要定義下界:
class Animal {} class Bird extends Animal {} class Consumer[+T](t: T) { def use[U >: T](u : U) = {println(u)} }
這個地方比較複雜, 簡單的說就是Scala內部實現是, 把類中的每一個能夠放類型的地方都作了分類(+, –, 中立), 具體分類規則不說了 對於這裏最外層類[+T]是協變, 可是到了方法的類型參數時, 該位置發生了翻轉, 成爲-逆變的位置, 因此你把T給他, 就會報錯說你把一個協變類型放到了一個逆變的位置上
因此這裏的處理的方法就是, 他要逆變, 就給他個逆變, 使用[U >: T], 其中T爲下界, 表示T或T的超類, 這樣Scala編譯器就不報錯了 ##上界upper bounds
看一下逆變類中使用上界的例子:
class Animal {} class Bird extends Animal {} class Consumer[-T](t: T) { def get[U <: T](): U = {new U} }
能夠看到方法的返回值是協變的位置,方法的參數是逆變的位置。 所以協變類的類型參數能夠用在方法的返回值的類型,在方法的參數類型上必須使用下界綁定 >:。 逆變類的類型參數能夠用在方法的參數類型上,用作方法的返回值類型時必須使用上界綁定 <:。
綜合協變,逆變,上界,下界
一個綜合例子:
class Animal {} class Bird extends Animal {} class Consumer[-S,+T]() { def m1[U >: T](u: U): T = {new T} //協變,下界 def m2[U <: S](s: S): U = {new U} //逆變,上界 } class Test extends App { val c:Consumer[Animal,Bird] = new Consumer[Animal,Bird]() val c2:Consumer[Bird,Animal] = c c2.m1(new Animal) c2.m2(new Bird) }
##View Bound <%
Scala還有一種視圖綁定的功能,如
class Bird {def sing = {}} class Toy {} class Consumer[T <% Bird]() { def use(t: T) = t.sing }
或者類型參數在方法上:
class Bird {def sing = {}} class Toy {} class Consumer() { def use[T <% Bird](t: T) = t.sing } class Test extends App { val c = new Consumer() c.use(new Toy) }
它要求T必須有一種隱式轉換能轉換成Bird,也就是 T => Bird,不然上面的代碼會編譯出錯: No implicit view available from Toy => Bird. 加入一個隱式轉換,編譯經過。
import scala.language.implicitConversions class Bird {def sing = {}} class Toy {} class Consumer() { def use[T <% Bird](t: T) = t.sing } class Test extends App { implicit def toy2Bird(t: Toy) = new Bird val c = new Consumer() c.use(new Toy) }
##Context Bound
context bound在Scala 2.8.0中引入,也被稱做type class pattern。 view bound使用A <% String方式,context bound則須要參數化的類型,如Ordered[A]。 它聲明瞭一個類型A,隱式地有一個類型B[A],語法以下:
def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]
更清晰的一個例子:
def f[A : ClassManifest](n: Int) = new Array[A](n)
又好比
def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)
##參考 Scala中的協變,逆變,上界,下界等