java泛型表达式和方法工作原理小结
java编译器在处理泛型表达式和泛型方法时的原理,明确编程人员,编译器和虚拟机之间的分工。
java翻译泛型方法原理小结
在java的虚拟机中并不存在实际的泛型类,而是将所有的泛型类编译成一个原始类型(raw type),也就是所谓的类型擦除,简单的说,从程序员的角度来看普通的泛型类应该是一族类,而在虚拟机看来,所有泛型类都是一个原始类(不支持实际意义上的泛型类),而几乎所有的泛型功能都通过编译器来映射成字节码,而与虚拟机无关
之所以虚拟机不支持实际意义上的泛型类,是由于早期的泛型类是由继承产生的,这样做也同样带来一个好处,即不会产生如c++那样的代码膨胀。
当翻译一个泛型表达式时实际上编译器将翻译两条虚拟机指令:
- 调用原始类的方法
- 将原始类强制转换成
例如:
Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();
以上取自java核心技术,但是按照对之前文章的理解,在这两步之前应该还有类型检查,即当你使用<Employee>
时会检查buddies是否是Employee类型,当然这一部也会由编译器翻译成字节码完成,而无需程序猿了解原理。
当泛型方法被类型擦除的适合会出现一些复杂的问题。首先一个是对于超类方法的调用:
请看如下的示例:
class DateInterval extend Pair<Date>
{
public void setSecond(Date second)
{
if(second.compareTo(getFirst())>=0
supper.setSecond(second);
}
}
其中Pair是一个Date对象,需要覆盖这个方法的类第二个值永远不小于第一个值。当类型擦除的时候从虚拟机看来这个代码变成如下形式:
class DateInterval extend Pair
{
public void setSecond(Date second) { }
}
但是经过类型擦除的方法和原先的原始类型的方法有什么不同呢?
class DateInterval extend Pair
{
public void setSecond(Object second) { }
}
这显然是两个不同的方法,编辑器通过产生一个桥方法(bridge method)来调用我们希望调用的方法,类似如下:
public void setSecond(Object second)
{
setSecond((Date) second);
}
即对于<Date>
这样的标识会有以上的桥方法替程序员正确的处理多态。
即编译器将泛型方法进行类型擦除,并调用setSecond(Object)桥方法,在桥方法内会根据类型来转换来调用正确的setSecond(Date)方法。
当然这里还有第二个问题,如果调用的不是setSecond而是getSecond,如果桥方法同样合成getSecond会出现如下情况:
Date getSecond()
Object getSecond()
这样的情况对于编程人员来说通常是不允许的,即通过返回类型来标识不同的方法,但是对于虚拟机来说确实可以的。所以以上方法可以正常运行。
更多推荐
所有评论(0)