泛型的兼容性
因為泛型是在Java SE5的時候引入的,而為了兼容之前沒有泛型的代碼,而擦除是最好的兼容方法。
擦除的主要問題是將非泛化代碼從泛化代碼的的轉(zhuǎn)變過程,繼續(xù)使用,直至客戶端準備好用泛型重寫這些代碼。這個動機不會破壞現(xiàn)有的代碼。
擦除的代價也是顯著的,泛型不能顯示地引用運行時類型的操作之中,例如轉(zhuǎn)型,instanceof和new表達式,這是因為所有關(guān)于參數(shù)的類型信息都丟失了。
所以無論何時,當在編寫泛型代碼的時候,必須時刻提醒自己,只是看起來具有有關(guān)參數(shù)的類型信息而已。
如果編寫了以下的代碼段。
class Foo<T> {
T var;
}
那么,看起來當在創(chuàng)建Foo的實例時。
Foo<Cat> f = new Foo<Cat>();
class Foo中的代碼應(yīng)該知道現(xiàn)在工作于Cat之上,盡管如此,在編寫代碼的時候,就必須強烈地知道var只是一個Object類型。
擦除和遷移兼容性表明,使用泛型不是強制的。
class GenericBase<T> {
private T element;
public T getElement() {
return element;
}
public void setElement(T element) {
this.element = element;
}
}
class Derived1<T> extends GenericBase<T> {
}
class Derived2 extends GenericBase {
}
//class Derived3 extends GenericBase<?> {
// Strange error
//}
public class ErasureAndInheritance {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Derived2 d2 = new Derived2();
Object obj = d2.getElement();
d2.setElement(obj);// warning here
}
}
上面的代碼中,Derived2繼承了GenericBase,但是沒有任何泛型參數(shù),但是編譯器沒有發(fā)出警告。而警告在set()被調(diào)用的時候才出現(xiàn)。
為了關(guān)閉警告,Java提供了一個注解(在Java SE5版本前不被支持)
@SuppressWarnings("unchecked")
需要注意的是,這個注解應(yīng)該盡可能地被放置在可以產(chǎn)生這類警告的方法之上,而不是整個類上。當要關(guān)閉警告的時候,最好盡量地"聚焦,這樣就不會過于寬泛地關(guān)閉警告,而導(dǎo)致意外地屏蔽掉真正的問題。
而Derived3的錯誤意味著編譯器期待得到的是一個原生基類,而不是一個不確定類型的類。
當希望將類型參數(shù)不僅僅當做Object處理的時候,就需要付出額外的努力去管理邊界,并且與C++等語言獲得參數(shù)化類型相比,需要付出多得多的努力來獲得少得回報。這并不是說這些語言比Java更得心應(yīng)手,而是說它們的參數(shù)類型化機制比Java更加強大、更靈活。