上次跟你們聊了泛型的基礎用法,今天接著往下說 —— 泛型里還有個挺重要的概念叫 “通配符”,就是那個問號 “?”,很多人第一次見都懵:這玩意兒跟普通泛型有啥區別?為啥有時候非得用它不可?小索奇當初也卡這兒好久,后來拿實際例子一琢磨,才徹底明白它的用處。
先給你出個題:假如你寫了個方法,要打印所有 List 集合里的元素,不管這個 List 裝的是 String、Integer 還是別的類型,該咋寫?
你可能會想,直接用 List不就行了?比如這樣:
public static void printList(List list) {
for (T item : list) {
System.out.println(item);
}
}
這么寫沒問題,但還有個更簡潔的方式 —— 用通配符 “?”:
public static void printList (List<?> list) {
for (Object item : list) {
System.out.println (item);
}
}
看出區別沒?用通配符的話,不用定義泛型參數,代碼更短。而且這兩種寫法效果一樣,不管你傳 List還是 List,都能正常打印。那為啥要有兩種寫法呢?其實核心區別在于:如果方法里不需要用到泛型的具體類型,只用通配符就夠了;要是需要操作具體類型(比如給集合加元素),就得用。
比如你想寫個方法,給 List 里加一個默認元素,這時候用通配符就不行了:
// 這段代碼會編譯報錯!
public static void addDefault (List<?> list) {
list.add ("默認值"); // 報錯:無法確定?的類型,不能加 String
}
這背后的原因是啥呢?因為通配符 “?” 代表 “未知類型”,編譯器不知道這個 List 到底裝的是啥,自然不敢讓你隨便加元素 —— 萬一人家是 List,你硬塞個 String 進去,不就亂套了?
但用就能搞定:
public static void addDefault(List list, T defaultValue) {
list.add(defaultValue);
}
調用的時候指定類型就行,比如給 List加默認值:
List strList = new ArrayList<>();
addDefault (strList, "默認值"); // 沒問題
是不是一下子就懂了?簡單說就是:通配符適合 “只讀” 場景(比如打印集合),泛型參數適合 “讀寫” 場景(比如給集合加元素)。
除了單純的 “?”,通配符還有兩種常用寫法:<? extends T > 和 <? super T>,這倆也特容易搞混,小索奇當初記了好幾天才分清。
先看 <? extends T>,它代表 “T 及其子類”。比如 <? extends Number>,就包括 Integer、Double、Long 這些 Number 的子類。這種寫法的特點是 “能讀不能寫”(跟普通?類似,但多了類型限制)。比如你寫個方法,求所有 Number 類型集合的總和:
public static double sum (List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue ();
}
return total;
}
不管你傳 List還是 List,都能算總和,因為它們都是 Number 的子類。但還是不能往里面加元素,比如 list.add (10) 會報錯 —— 因為編譯器不知道具體是 Integer 還是 Double,怕加錯類型。
再看 <? super T>,它代表 “T 及其父類”。比如 <? super Integer>,就包括 Number、Object 這些 Integer 的父類。這種寫法的特點是 “能寫不能讀”(或者說讀出來是 Object 類型)。比如你想寫個方法,給 List 里加多個 Integer 元素:
public static void addIntegers (List<? super Integer> list) {
list.add (1);
list.add (2); // 沒問題,因為?至少是 Integer 類型
}
調用的時候,傳 List、List甚至 List都能行。但讀元素的時候,只能用 Object 接收:
for (Object obj : list) {
// 要想用 Integer,得手動強轉
Integer num = (Integer) obj;
}
小索奇之前做統計功能時,就用 <? super Integer> 存過數據 —— 既可以存到 Integer 列表,也能存到 Number 列表,靈活度特別高。
最后給你總結個小口訣,記起來更方便:“上界 extends 能讀不能寫,下界 super 能寫不能讀,通配符?只讀不寫,泛型讀寫都能搞”。
你們平時寫代碼時,有沒有用過通配符卻踩了坑的?比如想加元素加不進去,或者不知道該用 extends 還是 super?可以在評論區跟小索奇聊聊,咱們一起把泛型這點事兒徹底搞明白~
搜索即興小索奇,點擊關注,加入社區群聊,獲取更多好用工具和資源