泛型解決什麼問題?
- 編譯期類型安全:集合元素類型固定,避免運行時
ClassCastException。
- 消除強轉:
list.get(0) 直接返回泛型類型,無需 (String) list.get(0)。
- 代碼復用:一套邏輯服務多種類型(集合框架、工具方法)。
泛型三大定義形式
// 1. 泛型類
class Box { private T value; public T get() { return value; } public void set(T v) { value = v; } }
// 2. 泛型介面
interface Processor { R process(T input); }
// 3. 泛型方法(類可非泛型)
class Util {
public static void swap(T[] arr, int i, int j) { T tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
// 靜態方法不可使用類泛型參數,必須自行聲明
}
泛型與繼承:不變性
List
通配符:協變與逆變
| 語法 | 術語 | 含義 | 可讀 | 可寫 | 典型場景 |
extends T | 上界通配符 | T 或其子類 | ✅ (作為 T) | ❌ (除 null) | 生產者 - 從集合取數據 |
super T | 下界通配符 | T 或其父類 | ✅ (作為 Object) | ✅ (作為 T) | 消費者 - 向集合放數據 |
| 無界通配符 | 任意類型 | ✅ (Object) | ❌ (除 null) | 與類型無關操作 |
PECS 原則:Producer Extends, Consumer Super
// 複製元素:src 是生產者,dest 是消費者
public static void copy(List extends T> src, List super T> dest) {
for (T t : src) dest.add(t);
}
類型擦除:泛型的運行時真相
- 編譯後字節碼無泛型信息,
List 和 List 運行時同為 List(原始類型)。
- 編譯器自動插入強轉、橋接方法保證類型安全。
- 影響:無法
new T()、T[]、instanceof T、靜態成員用泛型、異常捕獲泛型。
- 補救:傳遞
Class (Type Token)、使用 Array.newInstance、ParameterizedType 反射獲取實際類型參數。
實戰最佳實踐
- API 設計優先使用泛型,而非
Object。
- 方法簽名盡量使用通配符增加靈活性(
Collection extends E>)。
- 避免原始類型,啟用編譯器
-Xlint:unchecked。
- 文檔說明類型參數約束(
@param 必須實現 Comparable)。
- 嵌套泛型用類型別名簡化:
type Table = Map>; (Java 10+ var/局部變量類型推斷)。