JDK 1.5 前,集合存儲的是 Object,利用多態接受任意對象。但這帶來兩大痛點:
String s = (String) list.get(0); 繁瑣且易出錯。Integer 進「字串列表」,運行時才拋 ClassCastException。// JDK 1.4 風險代碼
List list = new ArrayList();
list.add("alpha");
list.add(new Integer(1234)); // 編譯通過,運行崩潰
String s = (String) list.get(1); // ClassCastException
泛型 將類型參數化,把類型檢查前置到編譯期,實現「類型安全」與「代碼復用」雙贏。
設計者在類/接口/方法定義處聲明形式類型參數(如 <E>),使用者實例化/調用時傳入實際類型參數(如 <String>),編譯器自動完成替換與檢查。
// 設計者視角:ArrayList 定義 public class ArrayListimplements List { public boolean add(E e) { ... } public E get(int index) { ... } } // 使用者視角:實例化時指定具體類型 ArrayList list = new ArrayList<>(); list.add("Hello"); // 編譯器檢查:只能放 String String s = list.get(0); // 無需強轉,直接拿 String
E - Element(集合元素)T - Type(任意類型)K, V - Key, Value(鍵值對)N - Number(數值類型)S, U, V - 第 2、3、4 個類型參數// 定義 public class GenericBox{ private E content; public GenericBox(E content) { this.content = content; } public E getContent() { return content; } public void setContent(E content) { this.content = content; } } // 使用 GenericBox box1 = new GenericBox<>("Hello"); GenericBox box2 = new GenericBox<>(123); // 自動裝箱 GenericBox box3 = new GenericBox<>(55.66);
關鍵機制:類型擦除 —— 編譯器將泛型類型替換為 Object(或上界),僅在調用處插入檢查與轉型碼,確保字節碼兼容性。一份字節碼服務所有具體類型,這與 C++ 模板「每種類型生成一份代碼」本質不同。
通配符形式 含義 適用場景 限制 List> / List extends Object>任意類型(非受限) 只讀操作( get返回Object)不能 add任何非 null 元素List extends Number>Number 及其子類(上界) 讀取數值型數據 不能 add具體子類對象List super Integer>Integer 及其父類(下界) 寫入 Integer 及其子類 get只能作Object用// T 必須實現 Comparablepublic static > T max(T a, T b) { return a.compareTo(b) > 0 ? a : b; } // 編譯器視 T 為 Comparable,插入轉型 max(55, 66); // Integer → Comparable max(6.6, 5.5); // Double → Comparable max("Mon", "Tue"); // String → Comparable 總結:泛型掌握清單
- 理解 類型擦除 本質:運行期無泛型信息,編譯期插檢查/轉型。
- 熟練定義 泛型類/接口/方法,遵循命名慣例。
- 靈活運用 三類通配符 解決 API 靈活性與類型安全的平衡(PECS 原則:Producer Extends, Consumer Super)。
- 善用 受限泛型 獲取上界能力,編寫通用算法(比較、排序、數學運算)。
- 注意 基本類型不能作類型參數,依賴自動裝箱/拆箱(
List❌ →List✅)。