在封裝底層框架時,泛型的某些「隱性規則」常導致編譯報錯或運行時 ClassCastException。本文結合 Oracle 官方文檔與實戰案例,系統講解 泛型 vs 重載選型、基本類型自動裝箱、聲明位置與作用域、多重邊界、通配符三大用法、類型擦除副作用,助你寫出類型安全、可讀性高的泛型代碼。
List.add(E));SharedPreferences.getString/getInt、StringBuilder.append(int/long/String))。若強行用泛型處理「同參數異邏輯」,會充斥 instanceof 判斷與強轉,代碼質量急劇下降。
MyClass<Integer> ok = new MyClass<>(); // √ MyClass<int> err = new MyClass<>(); // × 編譯錯誤
原因:類型參數 T 隱含繼承 Object,基本類型非對象。調用時 printT(100) 觸發 **自動裝箱** 為 Integer,底層仍是對象操作。
| 聲明位置 | 語法示例 | 作用域 | 備註 |
|---|---|---|---|
| 類級 | class MyClass<T> { } | 整個類(字段、方法、內部類) | 最常用 |
| 方法級 | public <T> T printT(T t) { } | 僅該方法 | 必須放在修飾符後、返回類型前;會遮蓋同名類級泛型 |
class MyClass<T> {
public <T> T printT(T t) { ... } // 方法級 T 遮蓋類級 T
}
MyClass<String> obj = new MyClass<>();
obj.printT(100); // 此處 T 推導為 Integer,與類級 String 無關
<T extends Number> → T 只能是 Number 或其子類;<T extends A & B & C> → A 可以是類或接口,**B、C 必須是接口**,且類(若有)必須寫在第一位;super 關鍵字用於類/方法聲明(僅用於通配符)。| 通配符 | 含義 | 典型場景 | 讀/寫權限 |
|---|---|---|---|
<?> | 無界通配符 | Class<?>、List<?> 只調用 Object 方法 | 可讀(作 Object),不可寫 |
<? extends T> | 上界通配符 | 作為**生產者**(Producer)只讀取,如 List<? extends Number> | 可讀(作 T),不可寫(編譯錯誤) |
<? super T> | 下界通配符 | 作為**消費者**(Consumer)只寫入,如 List<? super Integer> | 可寫(作 T),讀出只能作 Object |
PECS 原則:Producer Extends, Consumer Super。只讀用 extends,只寫用 super,既需讀又需寫**不要用通配符**,直接用具體類型。
new ArrayList<String>() 運行時只是 ArrayList;new T()、T[]、instanceof T、靜態字段/方法引用泛型;Class<T> clazz 參數、用 TypeToken (Gson) 保留泛型信息、@SuppressWarnings("unchecked") 精確壓制必要位置警告。public <T> 位置,別寫成 public T <T>;public abstract,泛型聲明同普通方法;private <E> void addAllHelper(List<? extends E> src, List<E> dest) 解決 ? extends E 無法寫入的問題。Java 泛型本質是 **編譯期類型檢查 + 類型擦除**。掌握「重載 vs 泛型選型邏輯、基本類型裝箱、聲明位置作用域、多重邊界語法、PECS 通配符原則、擦除副作用與補救」,能讓你在設計 API、封裝框架、排查 ClassCastException 時擁有清晰的心智模型,寫出既安全又優雅的泛型代碼。