前言
Kotlin 的泛型與 Java 一樣,都是一種語法糖,只在源代碼里出現,編譯時會進行簡單的字符串替換。
泛型是靜態類型語言中不可缺少的一部分,Kotlin 的泛型定義和使用都類似 Java,但也有一些基于工程實踐考慮的優化和改進。
泛型(Generics)其實就是把類型參數化,真正的名字叫做 類型參數,它給強類型編程語言加入了更強的靈活性。在 Java 中,只要是有類型的 API 元素,都可以泛型化,也就是泛型類、泛型接口、泛型方法和泛型屬性,泛型類和泛型接口可以統稱為泛型類型。其中最重要的是泛型類型和泛型方法,Kotlin 泛型系統繼承了 Java 泛型系統,同時添加了一些強化的地方。
實化泛型參數
在 Java 中經常會定義這種方法:
<T> void someFunction(Class<T> clazz) { //...}
這是因為 Java 的泛型擦除機制無法使用形如 T.class
來獲取泛型的真實類型對象。但是在調用者看來,泛型卻是實實在在的固定類型,所以這里借助 Kotlin 的內聯函數 inline 可以實化泛型參數,在 Kotlin 中只需要這樣:
fun <T> someFunction() { val clazz = T::class.java }
泛型的協變、逆變
在 Java 中,定義帶泛型的參數時為了更好的匹配目標類型,有 ? extends Type 和 ? super Type 兩種形式,以 List 接口中的定義為例:
boolean addAll(Collection<? extends E> c);void sort(Comparator<? super E> c);
addAll 方法中,Collection 中的泛型被定義成接收類型參數 E 的子類,這是因為需要讀取也就 c 的值,所以需要保證 c 是 Collection 的子類;而 sort 方法中,則是需要類中的類型參數 E 能夠被 Comparator 中的方法傳入,所以也就需要保證 E 是 Comparator 類型參數的子類。
而 Kotlin 中,針對于這兩種情況給了另外兩個關鍵字:需要讀取帶泛型對象的值時,使用 out 來標記類型參數;需要傳入類型參數的類型作為形參時,使用 in。
這兩種關鍵字的命名的方向是不同的:Java 偏向于從原理的方向命名,而 Kotlin 的命名對于具體的使用場景更為直觀。在 Kotlin 中,被 out 標記類型參數的類型稱之為協變類型,它代表當 A 是 B 的子類時,C 也能作為 C 的子類使用;而被 in 標記類型參數的類型則相反,它代表當 A 是 B的子類時,C 是 C 的子類。
從方法參數的使用上來說,Kotlin 和 Java 似乎沒有什么不同,而不同的地方在于 Kotlin 可以將這種定義作用在類型定義上,官方稱之為聲明點變型;與之相對應的,像 Java 這種在方法參數上定義的被稱為使用點變型。
聲明點變型在類型的聲明時定義了該類型參數是用在入參還是出參上,之后在這個類中所有用到的地方都會直接調用該類型的定義名稱來使用該類型的協變或者逆變。而 Java 中需要在每次使用時來重復說明該處需要協變還是逆變。Kotlin 也可以進行使用點變型,只要和 Java 一樣,在聲明處不進行說明,而只在使用時聲明就可以了。
「*」投影
因為 Kotlin 源碼中不允許忽略泛型參數,所以在一些泛型不重要的地方,就不可避免的使用 來表示。當使用 時,為了保證類型安全,官方建議的模式是將泛型定義為 的對象封裝起來,寫操作一般是安全的,因為 可以接收一切類型;對于讀操作可以進行安全的轉換,對于不匹配的類型進行統一處理。
泛型的注意點:
在java編程中類型系統最棘手的一部分是通配符類型。但是,在Kotlin編程中,是沒有通配符,采用聲明變化和類型投影來替代。
通配符的作用: 使用界限通配符增加API的靈活性。
在Java編程中一個常見的問題:
List<String> strs = new ArrayList<String>();List<Object> objs = strs; // !!! 這里會導致一個問題,在Java中是禁止這樣做的objs.add(1); //將integer類型的數據添加到String 類型的列表中String s = strs.get(0); // !!! ClassCastException異常 : Integer類型不能轉成String
在Kotlin編程中:
一旦聲明類型后,不能加入其它的類型數據,如下圖所示。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。
新聞熱點
疑難解答