J2SE 1.5(Tiger)將納入泛型類型(JSR-14 原型已可下載)。第一部分介紹基礎與重要性,本文詳細說明 Tiger/JSR-14 禁止的五大使用限制,並揭示其根源:型別擦除實現策略的必然結果。
instanceof 中使用「外露」類型參數new 操作中使用「外露」類型參數implements/extends 子句中使用「外露」類型參數JVM 不支援泛型,編譯器「耍花招」:用泛型資訊檢查代碼,隨即擦除所有泛型生成僅含普通型別的類別檔。List<T> 擦除為 List,「外露」參數(單獨出現的參數)擦除為其界限(T → Object)。此技術兼容性極強(新舊代碼無縫協作),但引入型別系統缺陷,限制泛型使用安全性。
編譯器完全禁止靜態方法/靜態內部類引用封閉類型參數。靜態欄位更隱蔽:JSR-14 1.0/1.2 允許引用但共享單一靜態欄位,導致運行時 ClassCastException 甚至無轉型代碼:
class C<T> { static T member; C(T t){ member=t; } T getMember(){return member;} }
C<String> c = new C<String>("test");
new C<Integer>(new Integer(1)); // 覆寫共享 member
c.getMember().toString(); // 拋出 ClassCastException!編譯器插入隱含轉型失敗
此破壞型別系統健全性。JSR-14 1.3 已修正為非法,Tiger 應同步禁止。
型別參數擦除為界限,界限不能是基本型別,故基本型別實例化擦除後無意義。例:Hashtable<String, int> 不合法,必須用 Hashtable<String, Integer> 並手動裝箱。自動裝箱/拆箱未納入 Tiger(期待 1.6)。
對外露參數轉型或 instanceof 產生 unchecked 警告。因擦除為界限,轉型實為向界限轉型,instanceof 檢查亦為界限檢查。例:
class C<T> implements Registry {
Hashtable<Integer, T> values;
public void register(Object o) { values.put(new Integer(counter), (T)o); } // 警告
}
若對 C<JFrame> 呼叫 register("test"),轉型表面成功,實則污染資料,後續計算錯誤或無聲失敗。嚴肅對待警告,該代碼區域型別檢查失效,靠自律。
泛型是健壯代碼利器,誤用則反害健壯性且難診斷修正。下期將介紹後兩大限制及納入泛型系統必面臨的問題。