Java 泛型程式設計
1. 泛型類
public class Pair<T> { private T first; private T second; public void setSecond(T second){...} .... }
2. 泛型方法
class ArrAlg{ public static <T> T getMiddle(T... a){ return a[a.length/2]; } }
3. 型別變數的限定
public static <T extends BoundingType1 & BoundingType2 ...> T min(T[] a)....
這表示T應該是BoundingType型別的子型別。T和繫結型別可以是類,也可以是介面 。一個型別變數或萬用字元可以有多個限定,但限定中之多隻有一個類,這是因為單繼承,當有多個限定時,基類要寫在第一個,介面寫在後面。
4. 原始型別,任何一個泛型型別,都自動提供一個原始型別,原始型別的名字就是刪去型別引數後的型別名。例如Pair<T>, 原始型別就是Pair。
5. 泛型轉換
- 虛擬機器中沒有泛型,只有普通的類和方法
- 所有的型別引數都用它們的第一個限定型別替換,沒有寫明限定型別就用Object
- 合成橋方法保持多型。
- 為保持型別安全性,必要時插入強制型別轉換(例如呼叫泛型方法返回值)
5. 橋方法
假設有下列類繼承了Pair<Date>,
class DateInterval extends Pair<Date> { public void setSecond(Date second){ .... } }
實際擦除型別之後
class DateInterval extends Pair { public void setSecond(Date second){...} //因為型別擦除,實際還存在一個繼承於Pair類的方法,顯然這是兩個方法 public void setSecond(Object second){ //實際生成橋方法,這裡會呼叫setSecond(Date seconde);即: setSecond((Date)second); } }
當如下呼叫時 Pair<Date> pair = new DateInterval(...); pair.setSecond(aDate);
。Pair型別只有一個方法setSecond(Object)。實際引用DateInterval類,因而將會呼叫DateInterval.setSecond(Object)。編譯器為了呼叫最合適的方法,實際上生成了一個橋方法,如下 public void setSecond(Object second){ setSecond((Date) second) }
。
6. 約束與侷限性
- 不能用基本型別例項化型別引數
- 執行時型別查詢只適用於原始型別
-
不能建立引數化型別的陣列,即不能
new Pair<String> [10]
- 不能例項化型別變數,即不能使用 new T(...), new T[] 或T.class這類的表示式。可以通過反射例項化T但不能T.class.newInstance(); 可以如下設計API來實現。
public static <T> Pair<T> makePair(Class<T> cl){ try{ return new Pair<>(cl.newInstance(), cl.newInstance()) }catch(Exception e){return null;} }
-
泛型類的靜態上下文中不能引用型別變數,例如
private static T aData; 或者 public static T fun(){}都是錯誤的。
- 不能丟擲或捕獲泛型類的例項
7. 萬用字元
- 萬用字元限定: extends 、super
例:Pair<? extends Person>表示泛型Pair型別,它的引數是Person的子類。
在這種情況下,getter和setter區別,getter可以正常呼叫,但是setter不行,會產生編譯錯誤,因為編譯器不能確定要傳入引數的型別,也沒法代替。
?extends Person getFirst() void setFirst(? extends Person)
反之,萬用字元的超型別限定: ?super Student
void setFirst(? super Student); ? super Student getFirst();
編譯器雖然不知道setFirst的確切型別,但是可以用任意Student物件呼叫,而不能用Person物件呼叫。如果呼叫getFirst,返回的物件不能保證,只能賦給Object。
- 無限定萬用字元 Pair<?>
? getFirst(); void setFirst(?);
getFirst的返回值只能賦給一個Object, setFirst不能呼叫除非setFirst(null),對於一些簡單操作非常有用,例如判定是否為null getFirst() == null
- 萬用字元捕獲 可以通過泛型方法捕獲萬用字元
- 記一次批量更新整型型別的列 → 探究 UPDATE 的使用細節
- 編碼中的Adapter,不僅是一種設計模式,更是一種架構理念與解決方案
- 執行緒池底層原理詳解與原始碼分析
- 30分鐘掌握 Webpack
- 線性迴歸大結局(嶺(Ridge)、 Lasso迴歸原理、公式推導),你想要的這裡都有
- Django 之路由層
- 【前端必會】webpack loader 到底是什麼
- day42-反射01
- 中心化決議管理——雲端分析
- HashMap底層原理及jdk1.8原始碼解讀
- 詳解JS中 call 方法的實現
- 列印 Logger 日誌時,需不需要再封裝一下工具類?
- 初識設計模式 - 代理模式
- 設計模式---享元模式
- 密碼學奇妙之旅、01 CFB密文反饋模式、AES標準、Golang程式碼
- [ML從入門到入門] 支援向量機:從SVM的推導過程到SMO的收斂性討論
- 從應用訪問Pod元資料-DownwardApi的應用
- Springboot之 Mybatis 多資料來源實現
- Java 泛型程式設計
- CAS核心思想、底層實現