內容參考瞭如下兩篇博客:
http://www.cnblogs.com/lemontea/archive/2013/02/17/2915065.html
http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.htmlhtml
假設有這樣兩個類型:TSub是TParent的子類,顯然TSub型引用是能夠安全轉換爲TParent型引用的。若是一個泛型接口IFoo<T>,IFoo<TSub>能夠轉換爲IFoo<TParent>的話,咱們稱這個過程爲協變,並且說這個泛型接口支持對T的協變。而若是一個泛型接口IBar<T>,IBar<TParent>能夠轉換爲T<TSub>的話,咱們稱這個過程爲反變(contravariant),並且說這個接口支持對T的反變。所以很好理解,若是一個可變性和子類到父類轉換的方向同樣,就稱做協變;而若是和子類到父類的轉換方向相反,就叫反變性。java
kotlin中有out和in關鍵字來表示協變和逆變,咱們經過out的兩個來認識什麼是逆變:
1. 泛型只能在返回值中出現
2. 只能進行子類向父類的轉型安全
eg: //有以下兩個類 //1.不支持逆變與協變 MyFuncA<T> //2.支持協變 MyFuncB<out T> //現對其進行初始化而後轉型 MyFuncA<object> funcAObject = null; MyFuncA<string> funcAString = null; MyFuncB<object> funcBObject = null; MyFuncB<string> funcBString = null; funcAObject = funcAString;//編譯失敗,MyFuncA不支持逆變與協變 funcBObject = funcBString;//變了,協變 funcBObject = funcBInt;//編譯失敗,值類型不參與協變或逆變
代碼中能夠看出使用了協變的泛型對象MyFuncB<out T>能夠進行子類向父類的轉換,而不支持逆變和協變得MyFuncA<T>則不容許向上或者是向下的轉換。ide
其實以上的兩條含義只是一條,只不過在不一樣的場景下表現不同而已,咱們一塊兒來看一下:函數
假設有這樣一個方法:學習
String Base<out T> { void Test(T t) }
泛型協變的,但咱們容許有方法能夠在參數中使用泛型(實際上這樣是不行的,這裏咱們經過反正法證實來證實這一結論)編碼
Base<object> BaseObject = null; Base<string> BaseString = null; BaseObject = BaseString; BaseObject.Test("");
咱們來看一下函數的調用過程:code
BaseObject被BaseString初始化,因此
BaseObject.Test("")的調用實質上是調用BaseString.Test(""),而BaseString中要的泛型T是string,而實際BaseObject給出的泛型T是object,
object沒法向下轉型爲string,所以出現類型轉換的異常。htm
逆變性反之也是同樣的推導,因爲進行的是父類向子類的轉型,在返回值返回的時候要求的是子類的泛型,但其實是調用父類的方法返回了父類,一樣出現了向下轉型的錯誤,所以逆變性中泛型只能在傳入參數中使用,不能在返回值中使用。對象
eg:
//過程同上 T Base<in T>.Test()
泛型逆變的,但咱們容許有方法能夠在返回值中使用泛型(實際上這樣是不行的,這裏咱們一樣經過反正法證實來證實這一結論)
Base<object> BaseObject = null; Base<string> BaseString = null; BaseString = BaseObject ; BaseString.Test();
只要按照協變時的調用方法看代碼的調用就會發現咱們在返回值的時候獲得的是object,而咱們要的是string,一樣出現向下轉型的錯誤。