本文靈感來源:羣友提出泛型相關的問題,感受不少人對泛型並非很瞭解~
Kotlin中的泛型和Java中的泛型其實大同小異,只是語法稍微有些不一樣。
大部份內容摘取自:《Kotlin實用指南》,感興趣的能夠訂閱一波 ~java
泛型可用於類、接口和方法的建立,對應:泛型類,泛型接口和泛型方法,代碼示例以下:web
注意事項:安全
- 一、泛型的參數只能是「類類型」,不能是簡單數據類型(int, float等);
- 二、泛型能夠有多個泛型參數
Tips:泛型類型的命名不是必須爲T,也可使用其餘「單個大寫字母」,沒有強制的命名規範,但爲了便於閱讀,有一些約定成俗的命名規範:app
- 通用泛型類型:T,S,U,V
- 集合元素泛型類型:E
- 映射鍵-值泛型類型:K,V
- 數值泛型類型:N
和C#中的泛型不一樣,Java和Kotlin中的泛型都是假泛型,實現原理就是「類型擦除(Type Erasure)」。
Java編譯器在生成Java字節碼中是不包含泛型中的類型信息的,只存在於代碼編譯階段,進JVM前會被擦除。
不信?寫個簡單的代碼體驗下:函數
運行結果以下:學習
從輸出結果能夠看到得到的類型確實被擦除了,此時的「類類型」皆爲ArrayList;spa
問 → 那咱們定義的泛型類型(Integer, String)到底去哪了?
答 → 被替換成了「原始類型」(字節碼中的真正類型)
問 → 那原始類型具體是什麼類型?
答 → 「限定類型」,無限定的話都用Object替換。
問 → ???
答 → 且聽我娓娓道來~.net
仍是上面的代碼,進Java字節碼看看(View -> Show Bytecode)3d
的確,Integer和String都被替換成了Object,那這個「限定類型」呢?寫個例子試試:code
看下字節碼:
行吧,此時的「原始類型」爲「限定類型」即Animal類。
沒限定類型的,都替換成Object類,也使得咱們能夠經過一些操做,繞過泛型。
好比,咱們利用「反射」往Integer類型的List插入一個String值是不會報錯的:
運行結果以下:
若是不對泛型類型作限制,泛型類型可實例化爲任意類型的話,可能會產生某些安全隱患。爲了限制容許實例化的泛型類型,可在泛型類型後追加 extends 父類型,代碼示例以下:
有界泛型類型在必定程度上限制了泛型類型,提升了程序的安全性;由於定義了邊界,因此能夠調用父類或父接口的方法。
Java泛型自己「不變」的,不支持「協變」和「逆變」,是經過通配符(?)來實現的
<T>「泛型標識符」用於泛型「定義時」可理解爲「形參」;
<?>「通配符」用於泛型「實例化時」可理解爲「實參」。
代碼示例以下:
代碼示例以下:
上邊界通配符只能讀不能寫,改成下邊界通配符
Tips:看完這裏,估計有部分讀者仍是有點懵逼,這裏總結下:
- 不變 → 子類和父類不要緊,正常存取,不用通配符就好,Java集合自己就是不變的;
- 協變 → A是B的子類型,泛型<A>也是泛型<B>的子類型,只想取,用extends。
- 逆變 → A是B的子類型,泛型<B>也是泛型<A>的子類型,只想存,用super。
- PECS法則(Producer Extends,Consumer Super) 參數化類型是一個生產者,則使用:<? extends T> 若是它是一個消費者,則使用<? super T>
和Java泛型同樣,Kotlin中的泛型也是「不變的」,沒有「通配符類型」,但有兩個其餘的東西:「聲明處型變」(declaration-site variance) 與 「類型投影」(type projections)
其實就是用「out」和「in」關鍵字來替換
其實就是對應Java中的*通配符:
- Java中<?>等同於<* extends Object>
- Kotlin中<*>等同於out Any
在Kotlin中能夠經過下述四種方法獲取泛型的類型(前兩種Java也適用):
原理:匿名內部類的聲明在編譯時進行,實例化在運行時進行。代碼示例以下:
獲取運行時泛型參數類型,子類可得到父類泛型的具體類型。代碼示例以下:
定義一個擴展函數用於啓動Activity,代碼示例以下:
fun <T: Activity> FragmentActivity.startActivity(context: Context, clazz: Class<T>) {
startActivity(Intent(context, clazz))
}
// 調用
startActivity(context, MainActivity::class.java)
複製代碼
Kotlin中使用「inline」關鍵字定義一個內聯函數,配合「reified」具體化(類型不擦除),獲得使用泛型類型的Class。修改後的代碼示例以下:
inline fun <reified T : Activity> Activity.startActivity(context: Context) {
startActivity(Intent(context, T::class.java))
}
// 調用
startActivity<MainActivity>(context)
複製代碼
參考文獻: