// 源碼
class Box {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
// 擦除後 (JVM 看到)
class Box {
private Object value;
public void set(Object value) { this.value = value; }
public Object get() { return value; }
}
// 子類繼承泛型時的橋接方法
class StringBox extends Box {
// 擦除後父類 set(Object),子類需保持多態
// 編譯器自動生成橋接方法:
public void set(Object value) { set((String) value); } // 檢查類型後調用具體方法
public void set(String value) { super.set(value); } // 真實業務方法
}
擦除的直接後果
現象
原因
無 List.class,只有 List.class
運行時類型信息被擦除
靜態字段/方法不可用類型參數
泛型類所有實例共享一個 Class 對象
new T()、new T[10] 編譯錯誤
運行時不知道 T 具體類型
異常捕獲不可用泛型
catch (MyException e) 非法,JVM 無法區分
instanceof List 編譯錯誤
運行時只有 List
二、通配符與上下界:? extends / ? super
三種通配符形式
語法
名稱
含義
可讀取類型
可寫入類型
List<?>
無界通配符
未知類型,但確定是 Object 子類
Object
無 (null 除外)
List<? extends Number>
上界通配符
Number 或其子類
Number
無
List<? super Integer>
下界通配符
Integer 或其父類 (Object/Number)
Object
Integer 及其子類
PECS 原則 (Producer Extends, Consumer Super)
「頻繁讀取 (生產者) 用 extends,頻繁寫入 (消費者) 用 super」
// 生產者:從集合讀取元素 → 用 extends
public void printNumbers(List extends Number> nums) {
for (Number n : nums) { // 安全讀取為 Number
System.out.println(n.doubleValue());
}
// nums.add(1); // ❌ 編譯錯誤:未知具體子類型,不安全
}
// 消費者:向集合寫入元素 → 用 super
public void addIntegers(List super Integer> ints) {
ints.add(1); // ✅ 安全:Integer 是 Integer 及其父類的合法子類
ints.add(2);
// Number n = ints.get(0); // ❌ 只能讀取為 Object
Object o = ints.get(0); // ✅ 唯一安全讀取類型
}
// 既讀又寫 → 不用通配符,用具體類型
public void process(List ints) {
ints.add(3);
Integer i = ints.get(0);
}
三、泛型類型系統規則
子類型關係
固定類型參數:List 是 Collection 子類型 (接口繼承)
通配符展開:Collection extends Number> 子類型包括:
接口維度:List extends Number>、Set extends Number>
類型維度:Collection、Collection
混合:ArrayList、HashSet
多類型參數:獨立應用上述規則
經典錯誤修正
// ❌ 錯誤:List 不是 List
四、自定義泛型類/接口/方法設計
泛型類
public class Pair {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
// ❌ 錯誤:靜態方法/字段不可用類型參數
// private static K staticKey;
// public static K getStaticKey() { ... }
// ❌ 錯誤:無法創建類型參數數組
// public K[] toArray() { return new K[2]; }
// ✅ 正確:數組用 Object[] 或 List
public List keys() { return Arrays.asList(key); }
}
泛型方法 (類型參數在方法級別)
public class Util {
// 靜態泛型方法
public static T first(List list) {
return list.isEmpty() ? null : list.get(0);
}
// 多類型參數
public static Map newHashMap() {
return new HashMap();
}
// 帶上界
public static > T max(Collection coll) {
T max = null;
for (T t : coll) {
if (max == null || t.compareTo(max) > 0) max = t;
}
return max;
}
}
泛型接口
public interface Repository {
T findById(ID id);
List findAll();
T save(T entity);
void deleteById(ID id);
}
// 實現時具體化
public class UserRepository implements Repository { ... }
// 或保持泛型
public class JpaRepositoryImpl implements Repository { ... }
// JDK 7 鑽石操作符
Map> map = new HashMap<>(); // 推斷右側類型參數
// JDK 8 目標類型推斷
List list = Collections.emptyList(); // 推斷為 List
// JDK 10 var + 泛型
var list = new ArrayList(); // 推斷為 ArrayList
var map = new HashMap>(); // 必須顯式指定嵌套類型參數