Java 泛型採用類型擦除機制,編譯後泛型信息被擦除,導致以下代碼無法編譯:
public class GenericOverload {
// 編譯錯誤:擦除後簽名相同,均為 print(List)
public void print(List<Integer> list) { }
public void print(List<String> list) { }
}
使用 <? extends T> 或 <? super T> 讓編譯器識別為不同簽名:
public class GenericWildcard {
// 編譯通過:擦除後為 print(List<?>) 但編譯器保留泛型簽名差異
public void print(List<? extends Number> list) {
System.out.println("Number 子類列表");
}
public void print(List<? super Integer> list) {
System.out.println("Integer 父類列表");
}
public void print(List<?> list) {
System.out.println("任意類型列表");
}
}
| 場景 | 通配符 | 含義 | 典型用例 |
|---|---|---|---|
| 只讀取元素 | <? extends T> | 上界通配符,元素是 T 或其子類 | List<? extends Number> → 可讀 Number 及子類 |
| 只寫入元素 | <? super T> | 下界通配符,元素是 T 或其父類 | List<? super Integer> → 可寫 Integer 及子類 |
| 既讀又寫 | 不使用通配符 | 確切類型 | List<Integer> |
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (T item : src) {
dest.add(item); // 安全:src 產出 T,dest 消費 T
}
}
// 使用
List<Integer> ints = Arrays.asList(1, 2, 3);
List<Number> nums = new ArrayList<>();
copy(nums, ints); // List<Number> 是 List<? super Integer>,List<Integer> 是 List<? extends Number>
// 編譯器會捕獲具體類型
List<?> list = Arrays.asList("a", "b");
process(list); // 推導為 List<String>
private static <E> void process(List<E> list) {
E first = list.get(0); // 類型安全
}
new List<String>[10] 非法 → 使用 List<List<String>> 或 @SuppressWarnings("unchecked")list instanceof List<String> 非法 → 檢查原始類型 list instanceof List通配符是 Java 泛型擦除機制下的妥協產物,通過 ? extends T(生產者)和 ? super T(消費者)實現了協變與逆變,解決了方法重載衝突並增強了 API 靈活性。掌握 PECS 原則是編寫優雅泛型代碼的關鍵。