Java泛型的局限性一般都是由類型擦除引起的。
1. 不能使用基本類型實例化類型參數
描述:不可以定義類似於Piar<int>這樣的類。
原因:假設泛型類為Pair,類型擦除以后為Pair,也即Pair,如果定義Pair則會出錯,因為int不能賦予Object類型的變量。
解決辦法:使用Integer,Double, Boolean, Long,Short, Character, Byte, Float
2. 不能實例化類型參數
描述:不可以new T()
原因:假設T是String,然后你准備定義String string = new T(); 但編譯器類型擦除之后,這條定義就變成String string = new Object(); 這明顯不可以。
解決辦法:使用反射。
public static T createInstance(Class clazz) {
try {
T result = clazz.newInstance();
}
catch (Exception e) {
return null
}
}3. 不可以實例化類型變量的數組
描述:不可以定義T[] array = new T[10];
原因:類型擦除后上述定義變為Object[] array = new Object[10]; 這樣一來我們可以將任何類型賦予array[0], 比如array[0] = "123"; 編譯器不會報錯,但運行時就有可能會出錯了。
解決方法:使用反射。
public static > T[] maxTwo(Class arrayElementClazz) {
// Type safety: Unchecked cast from Object[] to T[]
return (T[]) Array.newInstance(arrayElementClazz, 2) ;
}
4. 不可以定義泛型類的數組
描述:不可以定義Pair[] pairs = new Pair[10];
原因:類型擦除以后變為Pair[] pars = new Pair[10]; 然后我們可以賦予pairs[0] = new Pair(); 沒有編譯錯誤,但存在運行時錯誤。
解決方法:使用ArrayList,ArrayList> pairs = new ArrayList>();
5.不可以對泛型類型進行類型檢測,轉化
描述:不可以用a instanceof Pair,也不可以(Pair)a
原因:類型擦除后上述代碼變為 a instanceof Pair; (Pair)a,這明顯不是我們的目的;同理對於Pair pair; pair.getClass()總是返回Pair.class;
PS: Pair stringPairs = ...; Pair intPairs = ...; 那么stringPairs.getClass() == intPairs.getClass() 返回值為true
6. 不可以拋出或捕獲泛型異常
描述:既不能拋出也不能捕獲泛型類的對象。實際上,甚至泛型類型擴展throwable都是不合法的。以下都是不合法的
public class Problem extends Exception {/*...*/}
public static void doWork(Class t) { try { do work } catch (T e) { Logger.global.info(...) }}但以下方法合法:
public static void doWork(T t) throws T {
try {
do work
}
catch (Throwable realCause) {
t.initCause(realCause);
throw t;
}
}
7. 泛型類的靜態類型變量/方法無效
描述:不可以在泛型類Pair中定義public static T singleInstance;也不可以定義public static T getSingleInstance();
原因:擦除后變成
public Pair {
public static Object singleInstance; public static object getSingleInstance();}
所以類型變量對於static元素並不起作用。
8. 類型擦除引發的沖突
public class NameClash {
public boolean equals(T value) {
return false ;
}
}
從這個類的定義中來看,存在兩個equals方法,一個是自身定義的public boolean equals(T value) {...},一個是從Object繼承的public boolean equals(Object obj) {...},但類型
擦除以后,前者方法成為了public boolean equals(Object value) {...},而在一個類中同時存在兩個方法名和參數一樣的方法是不可能的,所以這里引發的沖突是沒法通過編譯
器的。可以通過重新命名方法進行修正。
擦除引起的沖突還體現在另一點上,再看一段錯誤的代碼:
class Calendar implements Comparable {...}
class GregorianCalendar extends Calendar implements Comparable {...}
上述代碼是非法的,為什么?回顧一下類型擦除后,虛擬機會為Calendar 類合成橋方法,實現了Comparable獲得一個橋方法:
public int compareTo (Object o) {return compareTo((Calendar)o);}
而實現了Comparable在類型擦除后,虛擬機為GregorianCalendar合成一個橋方法:
public int compareTo (Object o) {return compareTo((GregorianCalendar )o);}
這樣一來在GregorianCalendar類中存在兩個一樣的方法,這是不允許的。