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