java中泛型擦除和多态的冲突及解决
泛型擦除
java的泛型是一种伪泛型,编译器会去除泛型信息,字节码不会包含泛型信息。
public class Client {
public static void main(String[] args) {
List<String> nameList = new ArrayList<>();
List<Integer> ageList = new ArrayList<>();
//编译后都会变成 List
System.out.println(nameList.getClass() == ageList.getClass());// true
}
}
JVM看到的都是List,所以结果为 true.
和多态的冲突
public class Client2 {
public static void main(String[] args) {
DateHolder dateHolder = new DateHolder();
dateHolder.setValue(new Date());
Date value = dateHolder.getValue();
System.out.println(value);
}
interface Holder<T> {
T getValue();
void setValue(T value);
}
static class DateHolder implements Holder<Date> {
private Date value;
@Override
public Date getValue() {
return this.value;
}
@Override
public void setValue(Date value) {
this.value = value;
}
}
}
定义一个泛型接口,定义一个子类实现泛型接口,泛型接口经过编译器擦除后会变成
static interface Holder {
public abstract Object getValue();
public abstract void setValue(Object obj);
}
但是子类重写的方法明明参数和返回值都是 Date 类型啊,这是怎么回事呢?
我们的本意是想将泛型接口变成
static interface Holder {
public abstract Date getValue();
public abstract void setValue(Date obj);
}
但编译器没有办法将泛型变成 Date ,只能变成 Object。但编译器使用桥方法实现了这一功能。反编译结果为
static class DateHolder implements Holder {
public Date getValue() {
return value;
}
public void setValue(Date value) {
this.value = value;
}
public volatile void setValue(Object obj) {
setValue((Date)obj);
}
public volatile Object getValue() {
return getValue();
}
private Date value;
DateHolder() {
}
}
编译器通过桥方法解决了泛型擦除和多态之间的冲突。可以看到子类实现中
public Date getValue() {
return value;
}
public volatile Object getValue() {
return getValue();
}
我们自己的编码中这样写是不能通过编译器检查的,但编译器自己是可以这样的,因为虚拟机是通过返回值和参数列表来区分方法的。