泛型是 JDK 5 引入的編譯期類型安全機制,**運行期類型資訊被擦除**,這是許多困惑的根源。
List<String> 非 List<Object> 子型別public static void accept(ArrayList<Object> al) { /* ... */ }
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
accept(list); // 編譯錯誤!List<String> 不能傳給 List<Object>
}
原因:編譯後兩者皆擦除為 ArrayList,但編譯器檢測到泛型參數不兼容而報錯。
List<?>:未知類型,只能讀取為 Object,**不能添加**元素 (除 null)。List<? extends Number>:上界通配符,代表 Number 或其子類,**只能讀**,不可寫 (協變)。List<? super Number>:下界通配符,代表 Number 或其父類,**可寫入 Number 及子類**,讀取只能作為 Object (逆變)。public static void test(List<?> list) {
for (Object o : list) System.out.println(o);
// list.add(new Object()); // 編譯錯誤
}
| 特性 | 泛型 | 數組 |
|---|---|---|
| 類型資訊 | 編譯期擦除 (非具體化) | 運行期保留 (具體化) |
| 協變性 | 不變 | 協變 |
| 存儲檢查 | 編譯期 | 運行期 (ArrayStoreException) |
Object[] objs = new String[10]; // 合法
objs[0] = new Integer(1); // 運行期拋出 ArrayStoreException
List<Object> list = new ArrayList<String>(); // 編譯錯誤
? extends T (生產者) 與 ? super T (消費者) —— **PECS 原則**。new List<String>[10] 非法),改用 List<List<String>>。