擦除的主要的正当理由是从非泛化代码到泛化代码的转变过程,以及在不破坏现有类库的情况下,将泛型融入到Java语言。擦除使得现有的非泛型客户端代码能够在不改变的情况下继续使用,直至客户端准备好用泛型重写这些代码。这是一个崇高的动机,因为他不会突然间破坏现有所有的代码。
擦除的代价是显著的。泛型不能用于显式地引用运行时类型的操作之中,例如转型,instanceof操作和new表达式。因为所有关于参数的类型信息都丢失了,无论何时,当你在编写泛型代码时,必须时刻提醒自己,你只是看起来好像拥有有关参数的类型信息而已。因此,如果你编写了下面这样的代码段:
Class Foo<T>{
T var;
}
那么看起来当你在创建Foo的实例时:
Foo<Cat> f=new Foo<Cat>();
Class Foo中的代码应该知道现在工作于Cat之上,而泛型语法也在强烈暗示:在整个类中的各个地方,类型T都在被替换。但是事实并非如此,无论何时,当你在编写这个类的代码时,必须提醒自己:“不,他只是个Object”
另外,擦除和迁移兼容性意味着,使用泛型并不是强制的,尽管你可能希望这样:
public class GenericBase<T>{
private T element;
public void set(T arg){
arg=element;
}
public T get(){
return element;
}
}
public class Derived1<T> extends GenericBase<T>{
}
public class Derived2 extends GenericBase{
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Derived2 d2=new Derived2();
Object obj=d2.get();
d2.set(obj);
}
}
Derived2继承自GenericBase,但是没有任何泛型参数,而编译器不会发出任何警告。警告在set()被调用时候才会出现。
为了关闭警告,Java提供了一个注解,我们可以在列表中看到它(这个注解在JavaSE5之前的版本中不支持);@SuppressWarnings("unchecked")
注意,这个注解被放置在可以产生这类警告的方法之上,而不是整个类上。当你要关闭警告时,最好是尽可能的“聚焦”,这样就不会因为过于宽泛的关闭警告,而导致意外地遮蔽掉真正的问题。
当你希望将类型参数不要仅仅当做Object处理时,就需要付出额外的努力来管理边界。并且与在C++,Ada和Eiffel这样的语言中获得参数化类型相比,你需要付出多得多的努力来获得少的少的回报。这就是说,对于大多数的编程问题而言,这些语言通常都会比Java更得心应手,这就是说,他们的参数化类型机制比Java的更灵活,更强大。