首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 泛型完全指南:定義、通配符、擦除與最佳實踐

     編輯:Java 相關知識

泛型核心概念

泛型允許在定義類、接口、方法時使用類型參數,實際使用時再指定具體類型。核心優勢:編譯期類型安全、消除強制轉型、代碼復用。

泛型類與接口定義

// 多類型參數
public interface List<E> extends Collection<E> {}
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {}

// 使用:菱形語法 (JDK 7+)
List<String> list = new ArrayList<>();
Map<Integer, String> map = new HashMap<>();

泛型繼承規則

// 1. 固化類型參數
public class StringList extends ArrayList<String> {}

// 2. 保持類型參數
public class MyList<T> extends ArrayList<T> {}

// 3. 擦除為原始類型 (不推薦)
public class RawList extends ArrayList {}

偽泛型與類型擦除

Java 泛型僅存在於編譯期,運行期 JVM 看到的是原始類型(擦除後的類型)。

List<String> a1 = new ArrayList<>();
List<Integer> a2 = new ArrayList<>();
System.out.println(a1.getClass() == a2.getClass()); // true
System.out.println(a1.getClass()); // class java.util.ArrayList

限制:靜態上下文中不能使用類型參數(因為類只有一份字節碼)。

class Test<T> {
    // 錯誤: static T field; static { T t; } static void foo() { T t; }
}

類型通配符

無界通配符 ?

List<?> 表示元素類型未知的列表。只能 get() 返回 Object,不能 add()(除 null)。

上界通配符 ? extends T

List<? extends Number>:元素是 Number 或其子類。只能讀,不能寫(生產者 extends)。

void printSum(List<? extends Number> list) {
    double sum = 0;
    for (Number n : list) sum += n.doubleValue(); // OK
    // list.add(10); // 編譯錯誤
}

下界通配符 ? super T

List<? super Integer>:元素是 Integer 或其父類。只能寫,讀出為 Object(消費者 super)。

void addIntegers(List<? super Integer> list) {
    list.add(10); // OK
    list.add(new Integer(20)); // OK
    Object obj = list.get(0); // 只能作為 Object 讀取
}

PECS 原則

Producer Extends, Consumer Super。
作為生產者(只讀)用 extends,作為消費者(只寫)用 super。
經典例子:Collections.copy(List<? super T> dest, List<? extends T> src)。

泛型方法

方法級別定義類型參數,放在修飾符與返回類型之間。

public static <T> void swap(T[] arr, int i, int j) {
    T tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
}
// 調用:swap(new String[]{"a","b"}, 0, 1); // T 推斷為 String

泛型方法可表達多參數類型關係,如 <T> T max(T a, T b)。

擦除與轉型細節

  • 原始類型:不帶類型參數的泛型類(如 List),等同擦除後類型(List<Object> 的擦除)。
  • 擦除發生:將泛型引用賦值給原始類型變量。
    List<String> gs = new ArrayList<>(); List raw = gs; // 擦除,raw 元素類型視為 Object
  • 橋接方法:編譯器自動生成橋接方法保證多態。
    class MyList<T> implements List<T> { public void add(T t) {} } → 字節碼中同時有 add(T) 和 add(Object)(橋接)。

實戰陷阱與對策

陷阱原因對策
無法創建泛型數組擦除導致運行期類型不匹配用 List<T> 或 T[] arr = (T[]) new Object[size] (需 @SuppressWarnings)
instanceof 檢查泛型運行期類型信息已擦除用 list instanceof List<?> 或檢查元素類型
異常捕獲泛型Catch 塊不能用類型參數捕獲具體異常類或基類
重載泛型方法擦除衝突擦除後簽名相同避免此類重載,改用不同方法名

最佳實踐清單

  1. 優先使用泛型集合替代原始類型集合。
  2. API 設計盡量使用有界通配符 (extends/super) 增加靈活性。
  3. 避免在同一方法中混用原始類型與泛型類型(易產生未檢查警告)。
  4. 利用 @SuppressWarnings("unchecked") 精確抑制不可避免的警告(如數組轉型)。
  5. 文檔化類型參數約束(如 <T extends Comparable<T>>)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved