轉自:http://fineqtbull.iteye.com/blog/477994#bc2364938app
有位je上的同窗來短信向我問起了Scala類型參數中協變、逆變、類型上界和類型下界的使用方法和原理,本身雖然也剛學不久,在主要調查了《Programing in Scala》的19章後,試着在下面作一個總結。若有錯誤之處還請各位指正。
先說說協變和逆變(實際上還有非變)。協變和逆變主要是用來解決參數化類型的泛化問題。因爲參數化類型的參數(參數類型)是可變的,當兩個參數化類型的參數是繼承關係(可泛化),那被參數化的類型是否也能夠泛化呢?Java中這種狀況下是不可泛化的,然而Scala提供了三個選擇,即協變、逆變和非變。下面說一下三種狀況的含義,首先假設有參數化特徵Queue,那它能夠有以下三種定義。
1)trait Queue[T] {}
這是非變狀況。這種狀況下,當類型S是類型A的子類型,則Queue[S]不可認爲是Queue[A]的子類型或父類型,這種狀況是和Java同樣的。
2)trait Queue[+T] {}
這是協變狀況。這種狀況下,當類型S是類型A的子類型,則Queue[S]也能夠認爲是Queue[A}的子類型,即Queue[S]能夠泛化爲Queue[A]。也就是被參數化類型的泛化方向與參數類型的方向是一致的,因此稱爲協變。
3)trait Queue[-T] {}
這是逆變狀況。這種狀況下,當類型S是類型A的子類型,則Queue[A]反過來能夠認爲是Queue[S}的子類型。也就是被參數化類型的泛化方向與參數類型的方向是相反的,因此稱爲逆變。
接着看一個例子。函數
上例的Library單例對象的printBookList方法使用了函數來取得書籍的內容。在Scala中函數也是對象,上述狀況下的函數有一個參數,實際上該參數是以下特徵的實例。spa
printBookList的info參數是Function1類型,而 Function1的-S類型參數是逆變,+T參數是協變。printBookList方法的assert(info.isInstanceOf[Function1[_, _]])語句能夠驗證這一點。從printBookList方法的定義能夠知道,info的S類型參數是Book,T類型參數是AnyRef。然而主函數中使用處則是Library.printBookList(getTitle),getTitle函數中對應的S是Publication,T是String。爲何能夠與printBookList原來的定義不一致呢,這就是協變和逆變的威力了。因爲-S是逆變,而Publication是Book的父類,因此Publication能夠代替(泛化爲)Book。因爲+T是協變,而String是AnyRef的子類,因此String能夠代替(泛化爲)AnyRef。如此一來,主程序的語句也就徹底正確了。scala
接下來講說類型的上界和下界,它們的含義以下。對象
1) U >: Tblog
這是類型下界的定義,也就是U必須是類型T的父類(或自己,本身也能夠認爲是本身的父類)。繼承
2) S <: Tget
這是類型上界的定義,也就是S必須是類型T的子類(或自己,本身也能夠認爲是本身的子類)。qt
接着使用前面的例子來講明>:和<:的使用方法。printBokkListByTrait方法實現了與printBookList相同的功能,但它是經過傳入特徵對象來實現的。也就是說,new GetInfoAction[Publication, String] {}和def getTitle(p: Publication): String是等價的,而GetInfoAction定義中使用>:和<:來代替了Function1中+和-。那是因爲>:使得Publication能夠代替Book,因爲<:使得String能夠代替AnyRef。string
那麼爲何Function1中的S是逆變而T是協變呢,那是由apply方法的格式而起的。apply方法的參數類型是S決定了S必定是逆變,而返回類型是T則決定了T是協變,這也是Scala語言的強制規定。
咱們再來刨根問底一下,那麼爲何Scala要有這種規定呢?這實際上和Liskov代替原理有關,它規定T類型是U類型的子類條件是,在U對象出現的全部地方均可以用T對象來代替。同時對於U和T中相同的方法定義,還必須保證T的參數類型需求的比較少,而T的返回類型提供得比較多。從本文的類子來看,參數類型Publication是Book的父類,因此需求的就比Book少;而返回類型String是AnyRef的子類,所提供的就比AnyRef多。以上就是def getTitle(p: Publication): String能夠替代info: Book => AnyRef的緣由,也是Scala定義協變和逆變規則的理論基礎。
歡迎來Scala圈子看看。
http://scalagroup.group.iteye.com/