您好,登錄后才能下訂單哦!
內容參考了以下兩篇博客:
http://www.cnblogs.com/lemontea/archive/2013/02/17/2915065.html
http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html
假設有這樣兩個類型:TSub是TParent的子類,顯然TSub型引用是可以安全轉換為TParent型引用的。如果一個泛型接口IFoo<T>,IFoo<TSub>可以轉換為IFoo<TParent>的話,我們稱這個過程為協變,而且說這個泛型接口支持對T的協變。而如果一個泛型接口IBar<T>,IBar<TParent>可以轉換為T<TSub>的話,我們稱這個過程為反變(contravariant),而且說這個接口支持對T的反變。因此很好理解,如果一個可變性和子類到父類轉換的方向一樣,就稱作協變;而如果和子類到父類的轉換方向相反,就叫反變性。
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>則不允許向上或者是向下的轉換。
其實以上的兩條含義只是一條,只不過在不同的場景下表現不一樣而已,我們一起來看一下:
假設有這樣一個方法:
String Base<out T>
{
void Test(T t)
}
泛型協變的,但我們允許有方法可以在參數中使用泛型(實際上這樣是不行的,這里我們通過反正法證明來證明這一結論)
Base<object> BaseObject = null;
Base<string> BaseString = null;
BaseObject = BaseString;
BaseObject.Test("");
我們來看一下函數的調用過程:
BaseObject被BaseString初始化,所以
BaseObject.Test("")的調用實質上是調用BaseString.Test(""),而BaseString中要的泛型T是string,而實際BaseObject給出的泛型T是object,
object無法向下轉型為string,因此出現類型轉換的異常。
逆變性反之也是一樣的推導,由于進行的是父類向子類的轉型,在返回值返回的時候要求的是子類的泛型,但實際上是調用父類的方法返回了父類,同樣出現了向下轉型的錯誤,因此逆變性中泛型只能在傳入參數中使用,不能在返回值中使用。
eg:
//過程同上
T Base<in T>.Test()
泛型逆變的,但我們允許有方法可以在返回值中使用泛型(實際上這樣是不行的,這里我們同樣通過反正法證明來證明這一結論)
Base<object> BaseObject = null;
Base<string> BaseString = null;
BaseString = BaseObject ;
BaseString.Test();
只要按照協變時的調用方法看代碼的調用就會發現我們在返回值的時候得到的是object,而我們要的是string,同樣出現向下轉型的錯誤。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。