scala-協變、逆變、上界、下界


測試源代碼

歡迎大家關注: scala工具庫 ,里面包含各種庫的測試用例和使用說明文檔說明文檔

協變逆變

當我們定義一個協變類型List[A+]時,List[Child]可以是List[Parent]的子類型。

當我們定義一個逆變類型List[-A]時,List[Child]可以是List[Parent]的父類型。

Scala的協變

看下面的例子:

    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

c不能賦值給c2,因為Covariant定義成不變類型。

稍微改一下:

    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

因為Consumer定義成協變類型的,所以Covariant[Bird]是Covariant[Animal]的子類型,所以它可以被賦值給c2。

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

這里Consumer[-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中的協變,逆變,上界,下界等

Scala的協變和逆變上界與下界

協變點和逆變點


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM