泛型是 JDK 1.5 引入的核心特性,實現了「參數化型別」,將型別檢查前置到編譯期,大幅提升程式碼安全性與復用性。本文從泛型類/介面/方法定義、通配符上下界、類型擦除機制到實戰避坑,帶你徹底掌握 Java 泛型精髓。
JDK 1.5 前 Comparable 定義為 compareTo(Object o),執行期才發現型別不匹配拋 ClassCastException。泛型版 Comparable<T> 讓編譯器直接攔截錯誤:
// 編譯期報錯:String 不能傳給 compareTo(Date)
Comparable<Date> cmp = new Date();
cmp.compareTo("2024-01-01"); // 錯誤:The method compareTo(Date) ...
public class Box<T> {
private T value;
public void set(T v) { value = v; }
public T get() { return value; }
}
// 使用:具體化型別參數
Box<String> box = new Box<>(); // 鑽石運算符 JDK 7+
box.set("Hello");
String s = box.get(); // 無需強轉
public interface Processor<T> {
void process(T t);
}
// 實現類指定具體型別
class StringProcessor implements Processor<String> { ... }
// 或保持泛型
class GenericProcessor<T> implements Processor<T> { ... }
public static <T> void printArray(T[] arr) {
for (T t : arr) System.out.print(t + " ");
System.out.println();
}
// 呼叫時型別推導
printArray(new Integer[]{1,2,3});
printArray(new String[]{"A","B"});
// 顯式指定型別(型別推導失敗時)
MyClass.<Double>printArray(new Double[]{1.1, 2.2});
泛型預設為不變:List<Integer> 不是 List<Number> 的子型別。通配符打破此限制:
<? extends T> —— 只讀場景// 可接受 List、List 等 Number 子類
public static double sum(List<? extends Number> list) {
double s = 0;
for (Number n : list) s += n.doubleValue(); // 只能讀,取出即為 Number
return s;
}
// list.add(1); // 編譯錯誤:無法寫入具體子類
<? super T> —— 只寫場景// 可接受 List
<?> —— 僅操作結構public static void printList(List<?> list) {
for (Object o : list) System.out.println(o); // 僅能當 Object 讀
}
Producer Extends, Consumer Super
作為生產者(只讀取元素)用extends;作為消費者(只寫入元素)用super。
List<String> 和 List<Integer> 編譯後皆為原始 List。list instanceof List<String> 編譯錯誤,只能 list instanceof List。List<String> ls = new ArrayList<>();
List<Integer> li = new ArrayList<>();
System.out.println(ls.getClass() == li.getClass()); // true
List<? extends E>),提升 API 靈活度。new T[10] 編譯錯誤,改用 List<T> 或 (T[]) new Object[10](有非受檢警告)。instanceof 檢查具體泛型型別。extends Throwable 限制)。@param <T> 說明型別參數語義。public abstract class BaseDao<T, ID> {
public abstract T findById(ID id);
public abstract List<T> findAll();
public abstract void save(T entity);
public abstract void delete(ID id);
}
// 具體實現
public class UserDao extends BaseDao<User, Long> { ... }
泛型將型別安全從執行期提前到編譯期,配合通配符上下界實現協變/逆變,是 Java 型別系統的基石。深入理解類型擦除機制,能避免「泛型在運行期消失」帶來的誤區。建議下一步:閱讀 java.util.Collections、java.util.stream.Stream 源碼,觀察標準庫如何大規模運用泛型設計優雅 API。