1.泛型擦除
虚拟机没有泛型类型对象——所有对象都属于普通类,所以无论何时定义一个泛型类型,都自动提供一个相应的原始类型,原始类型的名字就是删除类型参数后的泛型类型名。类型变量擦除有三种情况:
1.如果泛型类型的类型变量没有限定(<T>) ,那么我们就用Object作为原始类型;
例如:上一节我们使用Pair<T>的原始类型如下所示:
package generic; /** * 泛型类 * @author 堕落梦醒 * * @param <Object> */ public class Pair { private Object first; private Object second; public Object getFirst() { return first; } public void setFirst(Object first) { this.first = first; } public Object getSecond() { return second; } public void setSecond(Object second) { this.second = second; } public Pair(Object first, Object second) { this.first = first; this.second = second; } public Pair() { } }
2.如果有限定(<T extends XClass>),我们就XClass作为原始类型;
例如:
public class Interval<T extends Comparable> { private T lower; private T upper; }
这个类的原始类型如下所示:
public class Interval { private Comparable lower; private Comparable upper; }
3. 如果有多个限定(<T extends XClass1&XClass2>),我们就用第一个边界的类型变量XClass1类作为原始类型;
对于情况3,为了提供效率,我们应该讲标签接口(即没有方法的接口)放在列表的末尾。
2.翻译泛型表达式
当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。例如,下面这个语句序列
Pair<Employee> pair = new Pair<Employee>(); Employee emp = pair.getFirst();
擦除getFirst的返回类型后将返回Object类型。编译器自动插入Employee的强制类型转换。也就是说,编译器把这个方法调用翻译为两条虚拟机指令:
1)对原始方法pair.getFirst()调用
2) 将返回的Object类型强制转换为Employee类型
3.翻译泛型方法(泛型在子类继承中的问题)
package generic; import java.util.Date; public class DateInterval extends Pair<Date>{ public void setSecond(Date second) { super.setSecond(second); } }
DateInterval 这个类想重写父类的setSecond方法,但是对于编译器并没有重写。因为pair类泛型擦除后setSecond(T second)变成了setSecond(Object second).但是DateInterval 类泛型擦除后setSecond方法还是setSecond(Date second)。两个方法的参数类型不一样,明显没有达到重写的要求,是两个不同的方法。这样就不能实现多态性了。
那么java编译器是如何解决这个问题的呢?编译器会在DateInterval类中生成一个桥方法。重写setSecond(Object second)方法,然后再方法里面调用setSecond(Date second)。
public void setSecond(Object second){ setSecond((Date)second); }
这样在多态调用时,调用的就是这个方法,达到了我们所期待的效果。
但桥方法有时也会带来问题,假设DateInterval类中也重写了getSecond方法:那么在泛型擦除后,就会有两个getSecond方法:
Date getSecond(); //在DateInteInterval中定义的
Object getSecond(); //编译器定义的桥方法
不能这样编写java代码,它们的方法名相同并且参数也相同。但是,在虚拟机中,用参数类型和返回类型确定一个方法,因此,编译器可能产生两个仅返回类型不同的方法字节码,虚拟机能够正确地处理这一情况。
4.java泛型转换的总结:
1.虚拟机中没有泛型,只有普通的类和方法。
2.所有的类型参数都用他们的限定类型替换。
3.桥方法被合成来保持多态。
4.为保持类型安全性,必要时插入强制类型转换。