爲什麼需要泛型通配符?¶
在Java泛型中,我們常常會遇到需要處理一類集合的情況:比如,一個方法可能需要同時處理List<Integer>、List<Double>、List<Number>等不同類型的集合,但又不想爲每個類型單獨寫一個方法。這時候,泛型通配符就能派上用場了。
通配符用?表示,分爲兩種:上界通配符(? extends T)和下界通配符(? super T),它們能幫助我們統一處理不同子類型的泛型集合,同時保證類型安全。
一、上界通配符(? extends T)¶
什麼是上界通配符?¶
上界通配符表示:集合中的元素類型是T的子類或T本身。
語法:List<? extends T> 或 List<? extends Number>(假設T是Number)。
舉個例子¶
假設我們有一個類層次結構:Object是根類,Number繼承Object,Integer和Double繼承Number。
如果我們想寫一個方法,能打印所有Number類型(及其子類)的集合元素,可以這樣寫:
// 打印List中所有元素
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) { // 元素一定是Number類型,安全
System.out.println(num);
}
}
// 調用方法
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
List<Number> numberList = Arrays.asList(10, 20.5, 30);
printNumbers(intList); // 正確(Integer是Number子類)
printNumbers(doubleList); // 正確(Double是Number子類)
printNumbers(numberList); // 正確(Number是上界)
上界通配符的特點¶
-
只能獲取元素,不能添加元素
因爲編譯器不知道集合具體是哪個子類(如List<Integer>或List<Double>),如果嘗試添加元素,類型可能不匹配。
❌ 錯誤示例:list.add(new Integer(10));
✅ 正確:只能獲取元素,返回類型是T(如Number)。 -
返回元素類型安全
元素一定是T或其子類,因此可以用T類型接收(如Number)。
二、下界通配符(? super T)¶
什麼是下界通配符?¶
下界通配符表示:集合中的元素類型是T的父類或T本身。
語法:List<? super T> 或 List<? super Number>(假設T是Number)。
舉個例子¶
假設我們想寫一個方法,能往集合中添加Number類型及其子類(如Integer、Double)的元素,同時處理不同父類的集合(如List<Object>、List<Number>):
// 往集合中添加Number類型及其子類
public static void addNumbers(List<? super Number> list) {
list.add(new Integer(10)); // 正確(Integer是Number子類)
list.add(new Double(20.5)); // 正確(Double是Number子類)
list.add(new Number(30)); // 正確(Number是上界)
}
// 調用方法
List<Object> objList = new ArrayList<>();
List<Number> numList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
addNumbers(objList); // 正確(Object是Number的父類)
addNumbers(numList); // 正確(Number是Number的父類)
addNumbers(intList); // 正確(Integer是Number的子類,可作爲父類集合處理)
下界通配符的特點¶
-
只能添加元素,不能獲取具體類型元素
因爲集合可能是Object、Number等父類集合,元素類型不確定,只能返回Object。
❌ 錯誤示例:Number num = list.get(0);
✅ 正確:只能獲取Object類型元素。 -
添加元素類型安全
可以添加T或其子類,編譯器能保證類型匹配。
三、關鍵區別與場景總結¶
| 通配符類型 | 定義 | 核心特點 | 適用場景 |
|---|---|---|---|
? extends T |
元素是T的子類或T本身 | 只能獲取元素(返回T),不能添加元素(除null) | 需要讀取不同子類集合的元素 |
? super T |
元素是T的父類或T本身 | 只能添加元素(T或其子類),不能獲取具體類型元素(只能返回Object) | 需要往不同父類集合添加元素 |
四、避坑指南¶
- 上界通配符不能寫“T”,下界通配符不能寫“T”
通配符是“不確定類型”的佔位符,而T是類型參數(需明確指定類型)。例如:
// 錯誤寫法(上界通配符不能用T作爲返回類型)
public static <T extends Number> T getFirst(List<T> list) { ... }
// 正確寫法(用類型參數T)
- 避免過度使用通配符
如果方法只需要List<Integer>,直接使用List<Integer>更明確。通配符用於“未知具體類型”的場景。
五、一句話總結¶
- 上界通配符(
? extends T):處理“所有T的子類”,只讀不寫。 - 下界通配符(
? super T):處理“所有T的父類”,只寫不讀具體類型。
掌握這兩個規則,就能靈活應對Java泛型中不同子類/父類集合的統一處理問題了!