泛型类型在编译后会变成相应的原始类型。有限定的泛型,擦除成为限定的类型,没有限定的泛型,擦除成为Object。
1. 翻译泛型表达式
Pair<Employee> p = ...;
Employee e = p.getFirst();
- 调用方法
- 虚拟机Object类型强转成Employee类型
2. 翻译泛型方法
public static <T extends Comparable>T min(T[] a) {}
//擦除后
public static Comparable T min(T[] a) {}
3. 桥方法的使用
问题1:在继承具体泛型类的时候,使用如果重写了里面的方法,那么T就会重写成具体类型,重写就变成了重载,从而导致多态问题。这时候需要使用桥方法进行调用更加严格的类型来保证多态性。比如
public class DataPair extends Pair<LocalDate> {
public setSecondDate(LocalDate date) { // 这里使用LocalDate重写了T date
if (second.compareTo(getFirst()) >= 0)
super.setSecond(second);
}
}
但是由于类型擦除,会出现两个方法:
public setSecondDate(Object date) {}
public setSecondDate(LocalDate date) {}
重写的方法,就变成了重载方法;
解决方法是使用桥方法: 从父类继承的方法,调用更加严格的自类方法。这个方法只起到调用的作用,类似于桥,所以叫做桥方法。
public setSecondDate(Object date) {this.setSecondDate((Date) date);}
问题2:在一个方法覆盖另一个方法时,可以指定一个更加严格的返回类型,比如
父类中:
public Object clone() {}
子类中:
public Employee clone() {}
子类重写了父类的克隆方法,但是编译的时候会出现两个方法,一个就是桥方法,用来调用自类的clone方法。
桥方法:
public Object clone() {调用Employee的方法}
如此可以看到虚拟机可以根据返回值进行函数的重载,但是代码中无法根据这种情况进行判断。为什么呢?
- 为什么不可以根据返回值进行重载,因为调用的时候不会明确支出返回类型,编译器无法得知具体调用哪一个函数。
- 为什么JVM可以,因为JVM判断函数的方法是:方法签名=方法名+参数+返回值
总结
- 虚拟机中没有泛型,只有普通的类和方法,所有的类型参数被其限定类型替换。有以下机制用来确保
- 虚拟机的插入类型转化
- 桥方法生成保证多态