类型擦除补充
由于某些历史原因和其它种种原因,java的泛型的实现方式是类型擦除,编译器会在编译时检查类型是否匹配,但编译后类型信息就丢失了。
但并不是所有的泛型类的泛型参数最终都变成Object。
诸如List<? extends Number>和List<T extends Number>最终会使用其上界Number作为最终擦除的目标结果,List<? super Number>也会以上界作为结果。
对于
classMyBox<TextendsNumber> {
public T value;
publicvoidsetValue(T value){
this.value = value;
}
public T getValue(){
return value;
}
}
可以认为编译后,代码变为:
classMyNumberBox{
public Number value;
publicvoidsetValue(Number value){
this.value = value;
}
public Number getValue(){
return value;
}
}
publicvoiddoSomething(MyBox<? extends Number> box){
// do something
}
变为
publicvoiddoSomething(MyNumberBox box){
// do something
}
桥接方法
classMyBox<T> {
public T value;
publicvoidsetValue(T value){
this.value = value;
}
public T getValue(){
return value;
}
}
classChildBoxextendsMyBox<String> {
@Override
publicvoidsetValue(String value){
super.setValue(value + " <from child> ");
}
@Override
public String getValue(){
returnsuper.getValue() + " <from child> ";
}
}
由于java实现泛型的方式,导致这段代码的语义有些奇怪。既然Mybox最终会被类型擦除,那么里面保存的就都是Object,而子类却可以重写参数为String的父类中的方法。如果父类类型擦除了,那么理论上子类中应该有2个getter和2个setter,其中一个是父类传下来的操作Object,另一个是自己的操作String。那么这就不是重写而是重载,不符合我们的预期。那么真实情况真的是重载了吗?验证:
以下代码的第二行set方法是通过不了的
publicstaticvoidmain(String[] args){
ChildBox box = new ChildBox();
box.setValue("xiaoming");
box.setValue(new Object());
System.out.println(box.getValue());
}
实际上这是因为编译器做了黑魔法,它自动生成了一个操作Object的getter和setter,随后这个方法内部调用子类的getter和setter。这样就表现得多态了:
// 父类引用指向子类实例,实际调用子类方法,完美的重写,多态!
MyBox<String> box2 = new ChildBox();
box2.setValue("li si");
System.out.println(box2.getValue());