天天看点

Java泛型解析(03):虚拟机运行泛型代码

java泛型解析(03):虚拟机运行泛型代码

     java虚拟机是不存在泛型类型对象的,全部的对象都属于普通类,甚至在泛型实现的早起版本号中,可以将使用泛型的程序编译为在1.0虚拟机上可以执行的class文件,这个向后兼容性后期被抛弃了,所以后来假设用sun公司的编译器编译的泛型代码,是不能执行在java5.0之前的虚拟机的,这样就导致了一些实际生产的问题,如一些遗留代码怎样跟新的系统进行衔接,要弄明确这个问题,须要先了解一下虚拟机是怎么执行泛型代码的。

       虚拟机的一种机制:擦除类型參数,并将其替换成特定类型,没有指定特定类型用object取代,如前文中的couple<t>类,虚拟机擦除后:   

[code01]

     类型參数t是一个随意类型的,所以擦除后用object取代了。无论是couple<employee>或者couple<string>擦除后都成为了原始类couple类,这就好比回到了泛型引入java之前的普通类。所以这里重点环绕着擦除类型參数这个机制展开解说。

     如有对类型參数有类型限定会怎么替换呢?擦除类型參数机制告诉我们,使用限定的类型取代,假设有多个,使用第一个取代,看一段代码:

[code02]

     code02擦除后,period的原始类型例如以下:

[code03]

     思考一下,假设将period<t extends comparable<t> & serializable>写成period<t extends serializable  & comparable<t>>会是怎么样呢?同理,擦除后原始类型用第一个serializable取代,这样进行compareto方法调用的时候,编译器会进行必要的强制类型转换,所以为了提高效率,将标签接口(没有不论什么方法的接口,也叫tagging接口)放在后面。

     先来看看虚拟机运行表达式的时候发生了什么,如:

[code04]

     擦除后,getwife()返回的是object类型,然后虚拟机会插入强制类型转换,将object转换为employee,所以虚拟机实际上运行了两天指令:

     1.调用couple.getwife()方法。

     2.将object转换成employee类型。

     再来看看虚拟机运行泛型方法的时候发生了什么,泛型方法如:

[code05]

     可是泛型方法的擦除会带来两个复杂的问题,且看第一个实例,一个实例:

[code06]

     dateinterval类型擦除后,period中的方法变成:

     public void setbegin(object begin) {...}

     而dateinterval中的方法还是:

     public void setbegin(date begin) {...}

     所以dateinterval从period中继承了 public void setbegin(object begin) {...}而自身又存在

public void setbegin(date begin) {...}方法,用户使用时问题发生了:

[code07]

     这里由于period引用指向了dateinterval实例,依据多态性,setbegin应该调用dateinterval对象的setbegin方法,但是这个擦除让period中的 public void setbegin(object begin) {...}被调用,导致了擦除与多态发生了冲突,怎么办呢?虚拟机此时会在dateinterval类中生成一个桥方法(bridge method),调用过程发生了细微的变化:

[code08]

     有了这个合成的桥方法以后,code07中对setbegin的调用过程例如以下:

      1.调用dateinterval.setbegin(object)方法。

      2.dateinterval.setbegin(object)方法调用dateinterval.setbegin(date)方法。

     发现了吗,当我们在dateinterval中添加了getbegin方法之后会是什么样子的呢?是不是peroid中有一个object getbegin()的方法,而dateinterval中有一个date getbegin()方法呢,这两个方法在java中是不能同一时候存在的?但是java5以后添加了一个协变类型,使得这里是被同意的,看看dateinterval中getbegin方法就知道了:

[code09]

     这里用了@override,说明是覆盖了父类的object getbegin()方法,而返回值能够指定为父类中的返回值类型的子类,这就是协变类型,这是java5以后才干够同意的,同意子类覆盖了方法后指定一个更严格的类型(子类型)。

总结:

     1.记住一点,虚拟机中没有泛型,仅仅有普通的类。

     2.全部泛型的类型參数都用它们限定的类型取代,没有限定则用object。

     3.为了保持类型安全性,虚拟机在有必要时插入强制类型转换。

     4.桥方法的合成用来保持多态性。

     5.协变类型同意子类覆盖方法后返回一个更严格的类型。