在上篇例子中定義的 Queue 是一個(gè) Trait,而不是一個(gè)類型,這是因?yàn)?Queue 需要一個(gè)類型參數(shù)才能構(gòu)成一個(gè)類型, 也就是說(shuō)你不可以直接創(chuàng)建一個(gè)類型為 Queue 的對(duì)象:
scala> def doesNotCompile(q:Queue) {}
<console>:8: error: trait Queue takes type parameters
def doesNotCompile(q:Queue) {}
^
實(shí)際上,Queue 允許你指明類型參數(shù),比如 Queue[String],Queue[Int]或 Queue[AnyRef]等,因此 Queue 也可以稱為一個(gè)類型構(gòu)造器,因?yàn)槟憧梢灾该饕粋€(gè)參數(shù)類型后構(gòu)造一個(gè)新的類型。
Queue 也可以稱為一個(gè)通用 Trait(包含類型參數(shù)的類或 Trait稱為“generic”) ,而指明了類型參數(shù)之后就不再是通用類型,而是特定的類型了,比如Queue[String]等。
類型參 數(shù)和派生結(jié)合起來(lái)之后,就會(huì)產(chǎn)生一些有趣的問(wèn)題,比如 String 是 AnyRef 的子類,那么 Queue[String]和 Queue[AnyRef]之間會(huì)不會(huì)有什么繼承關(guān)系呢?
在 Scala 中,可以有三種不同的關(guān)系,缺省情況比如 Queue[T],Queue[String]和 Queue[AnyRef]不存在繼承關(guān)系。此外 Scala 還支持兩種關(guān)系: Covariance(協(xié)變關(guān)系)和 Contravariance(逆變關(guān)系),分別以 Queue[+T]和 Queue[-T] 代表。
Covariance(協(xié)變關(guān)系)是在類型前面使用“+”號(hào),表示如果兩個(gè)類型 T,S 如果 T 是 S 的子類,那么 Queue[T]也是 Queue[S]的子類
而 Contravariable(逆變關(guān)系)則相反,如果如果兩個(gè)類型 T,S 如果 T 是 S 的子類,反過(guò)來(lái),Queue[S]是 Queue[T]的子類。
我們以一個(gè)具體的例子來(lái)說(shuō)明一下,比較直觀,定義三個(gè)類
GrandParent ,Parent, Child :
class GrandParent
class Parent extends GrandParent
class Child extends Parent
class Box[+A]
class Box2[-A]
def foo(x : Box[Parent]) : Box[Parent] = identity(x)
def bar(x : Box2[Parent]) : Box2[Parent] = identity(x)
那么我使用下面的幾種調(diào)用方法來(lái)看看+A 和-A 的不同之處:
scala> foo(new Box[Child]) // success
res1: Box[Parent] = Box@5da444e4
scala> foo(new Box[GrandParent]) // type error
<console>:12: error: type mismatch;
found : Box[GrandParent]
required: Box[Parent]
foo(new Box[GrandParent]) // type error
^
scala> bar(new Box2[Child]) // type error
<console>:13: error: type mismatch;
found : Box2[Child]
required: Box2[Parent]
bar(new Box2[Child]) // type error
^
scala> bar(new Box2[GrandParent]) // success
res4: Box2[Parent] = Box2@59615389
[T],[+T],[-T]為類型參數(shù)的三種不同的變體。使用+,-稱為類型的變體標(biāo)識(shí)。
在純函數(shù)編程的世界中,很多類型存在非常明顯的協(xié)變關(guān)系,但是一但出現(xiàn)可變的數(shù)據(jù)時(shí),情況就發(fā)生了變化,比如我們看看下面的例子:
class Cell[T](init:T) {
private[this] var current = init
def get = current
def set(x:T) { current = x}
}
如果我們假定我們定義的是 Cell[+T]而不是上面例子中的 Cell[T](實(shí)際上編譯器會(huì)在使用 Cell[+T]時(shí)報(bào)錯(cuò),我們啦看看為什么?假定我們使用的是 Cell[+T]來(lái)定義,那么我們可以寫(xiě)如下代碼:
val c1 = new Cell[String]("abc")
val c2: Cell[Any] = c1
c2.set(1)
val s:String = c1.get
這四行代碼看起來(lái)都沒(méi)有錯(cuò)(假定我們使用的是 Cell[+T],那么 Cell[String] 是 Cell[AnyRef]的子類,因此 c2 可以使用 c1 賦值)。最后的結(jié)果我們是把整數(shù) 1 賦值給了這字符串類型,這就造成了類型不匹配,這也是為什么編譯器在使用 Cell[+T]會(huì)報(bào)錯(cuò):
<console>:10: error: covariant type T occurs in contravariant position in type T of value x
def set(x:T) { current = x}
要注意的是在 Scala 中,Array[T]不是協(xié)變關(guān)系的,因此 Array[String]不是 Array[Any]的子類。因此不可以直接把 Array[String]類型的變量賦值給 Array[Any]類型的變量,例如:
scala> val a1=Array("abc")
a1: Array[String] = Array(abc)
scala> val a2:Array[Any] = a1
<console>:8: error: type mismatch;
found : Array[String]
required: Array[Any]
Note: String <: Any, but class Array is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
val a2:Array[Any] = a1
但是有時(shí)需要這種賦值,Scala 允許你把特殊類型的數(shù)組強(qiáng)制轉(zhuǎn)換成其父類型的數(shù)組,例如:
scala> val a2 :Array[Object] = a1.asInstanceOf[Array[Object]]
a2: Array[Object] = Array(abc)