鍍金池/ 教程/ Scala/ Scala課堂:類型和多態(tài)類型(二)
Scala 課堂:高級(jí)類型(二)
Scala 課堂:基礎(chǔ)(三)
Scala 課堂:類型和多態(tài)類型(一)
Scala 課堂:基礎(chǔ)(二)
Scala課堂:基礎(chǔ)(五)
Scala 課堂:基礎(chǔ)(四)
Scala 課堂:基礎(chǔ)(六)
Scala 課堂:集合(二)
Scala課堂:類型和多態(tài)類型(二)
Scala 課堂:模式匹配和函數(shù)組合
Scala 課堂:高級(jí)類型(一)
Scala 課堂:集合(一)
Scala 課堂:基礎(chǔ)(一)

Scala課堂:類型和多態(tài)類型(二)

這里我們轉(zhuǎn)載 Twitter 的 Scala 課堂,轉(zhuǎn)載的內(nèi)容基本來自 Twitter 的 Scala 課堂中文翻譯,部分有小改動(dòng).

變性 Variance

Scala 的類型系統(tǒng)必須同時(shí)解釋類層次和多態(tài)性。類層次結(jié)構(gòu)可以表達(dá)子類關(guān)系。在混合OO和多態(tài)性時(shí),一個(gè)核心問題是:如果 T’ 是 T 一個(gè)子類, Container[T’] 應(yīng)該被看做是 Container[T] 的子類嗎?變性(Variance)注解允許你表達(dá)類層次結(jié)構(gòu)和多態(tài)類型之間的關(guān)系:

含義 Scala 標(biāo)記
協(xié)變covariant C[T’]是 C[T] 的子類 [+T]
逆變contravariant C[T] 是 C[T’]的子類 [-T]
不變invariant C[T] 和 C[T’]無關(guān) [T]

子類型關(guān)系的真正含義:對(duì)一個(gè)給定的類型 T,如果 T’ 是其子類型,你能替換它嗎?


scala> class Covariant[+A]
defined class Covariant

scala> val cv: Covariant[AnyRef] = new Covariant[String]
cv: Covariant[AnyRef] = Covariant@5d0c0c59

scala> val cv: Covariant[String] = new Covariant[AnyRef]
<console>:8: error: type mismatch;
 found   : Covariant[AnyRef]
 required: Covariant[String]
       val cv: Covariant[String] = new Covariant[AnyRef]
scala> class Contravariant[-A]
defined class Contravariant

scala> val cv: Contravariant[String] = new Contravariant[AnyRef]
cv: Contravariant[String] = Contravariant@40738293

scala>  val fail: Contravariant[AnyRef] = new Contravariant[String]
<console>:8: error: type mismatch;
 found   : Contravariant[String]
 required: Contravariant[AnyRef]
        val fail: Contravariant[AnyRef] = new Contravariant[String]

逆變似乎很奇怪。什么時(shí)候才會(huì)用到它呢?令人驚訝的是,函數(shù)特質(zhì)的定義就使用了它!


trait Function1 [-T1, +R] extends AnyRef

如果你仔細(xì)從替換的角度思考一下,會(huì)發(fā)現(xiàn)它是非常合理的。讓我們先定義一個(gè)簡單的類層次結(jié)構(gòu):


scala>  class Animal { val sound = "rustle" }
defined class Animal

scala>  class Bird extends Animal { override val sound = "call" }
defined class Bird

scala>  class Chicken extends Bird { override val sound = "cluck" }

假設(shè)你需要一個(gè)以 Bird 為參數(shù)的函數(shù):


val getTweet: (Bird => String) = // TODO

標(biāo)準(zhǔn)動(dòng)物庫有一個(gè)函數(shù)滿足了你的需求,但它的參數(shù)是 Animal。在大多數(shù)情況下,如果你說“我需要一個(gè),我有一個(gè)的子類”是可以的。但是,在函數(shù)參數(shù)這里是逆變的。如果你需要一個(gè)參數(shù)為 Bird 的函數(shù),并且指向一個(gè)參數(shù)為 Chicken 的函數(shù),那么給它傳入一個(gè) Duck 時(shí)就會(huì)出錯(cuò)。但指向一個(gè)參數(shù)為 Animal 的函數(shù)就是可以的:


scala>  val getTweet: (Bird => String) = ((a: Animal) => a.sound )
getTweet: Bird => String = <function1>

函數(shù)的返回值類型是協(xié)變的。如果你需要一個(gè)返回 Bird 的函數(shù),但指向的函數(shù)返回類型是 Chicken,這當(dāng)然是可以的。


scala>  val hatch: (() => Bird) = (() => new Chicken )
hatch: () => Bird = <function0>

邊界

Scala 允許你通過 邊界 來限制多態(tài)變量。這些邊界表達(dá)了子類型關(guān)系


scala>  def cacophony[T](things: Seq[T]) = things map (_.sound)
<console>:7: error: value sound is not a member of type parameter T
        def cacophony[T](things: Seq[T]) = things map (_.sound)
                                                         ^

scala>  def biophony[T <: Animal](things: Seq[T]) = things map (_.sound)
biophony: [T <: Animal](things: Seq[T])Seq[String]

scala>  biophony(Seq(new Chicken, new Bird))
res4: Seq[String] = List(cluck, call)

類型下界也是支持的,這讓逆變和巧妙協(xié)變的引入得心應(yīng)手。 List[+T] 是協(xié)變的;一個(gè) Bird 的列表也是 Animal 的列表。List 定義一個(gè)操作::(elem T)返回一個(gè)加入了 elem 的新的 List。新的 List 和原來的列表具有相同的類型:


scala> val flock = List(new Bird, new Bird)
flock: List[Bird] = List(Bird@7e1ec70e, Bird@169ea8d2)

scala> new Chicken :: flock
res53: List[Bird] = List(Chicken@56fbda05, Bird@7e1ec70e, Bird@169ea8d2)

List 同樣 定義了::[B >: T](x: B) 來返回一個(gè) List[B] 。請(qǐng)注意 B >: T,這指明了類型 B 為類型T的超類。這個(gè)方法讓我們能夠做正確地處理在一個(gè) List[Bird] 前面加一個(gè) Animal 的操作:


scala> new Animal :: flock
res6: List[Animal] = List(Animal@75be93a7, Bird@5ab69598, Bird@9175caf)

注意返回類型是 Animal。

量化

有時(shí)候,你并不關(guān)心是否能夠命名一個(gè)類型變量,例如:


scala>  def count[A](l: List[A]) = l.size
count: [A](l: List[A])Int

這時(shí)你可以使用“通配符”取而代之:


scala>  def count(l: List[_]) = l.size
count: (l: List[_])Int

這相當(dāng)于是下面代碼的簡寫:


scala>  def count(l: List[T forSome { type T }]) = l.size
count: (l: List[T forSome { type T }])Int

注意量化會(huì)的結(jié)果會(huì)變得非常難以理解:


scala> def drop1(l: List[_]) = l.tail
drop1: (l: List[_])List[Any]

突然,我們失去了類型信息!讓我們細(xì)化代碼看看發(fā)生了什么:


scala> def drop1(l: List[T forSome { type T }]) = l.tail
drop1: (l: List[T forSome { type T }])List[T forSome { type T }]

我們不能使用T因?yàn)轭愋筒辉试S這樣做。 你也可以為通配符類型變量應(yīng)用邊界:


scala>  def hashcodes(l: Seq[_ <: AnyRef]) = l map (_.hashCode)
hashcodes: (l: Seq[_ <: AnyRef])Seq[Int]

scala>  hashcodes(Seq(1,2,3))
<console>:9: error: the result type of an implicit conversion must be more specific than AnyRef
               hashcodes(Seq(1,2,3))
                             ^
<console>:9: error: the result type of an implicit conversion must be more specific than AnyRef
               hashcodes(Seq(1,2,3))
                               ^
<console>:9: error: the result type of an implicit conversion must be more specific than AnyRef
               hashcodes(Seq(1,2,3))
                                 ^

scala>  hashcodes(Seq("one", "two", "three"))
res8: Seq[Int] = List(110182, 115276, 110339486)