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

     Java虚拟机是不存在泛型类型对象的,全部的对象都属于普通类,甚至在泛型实现的早起版本号中,可以将使用泛型的程序编译为在1.0虚拟机上可以执行的class文件,这个向后兼容性后期被抛弃了,所以后来假设用Sun公司的编译器编译的泛型代码,是不能执行在Java5.0之前的虚拟机的,这样就导致了一些实际生产的问题,如一些遗留代码怎样跟新的系统进行衔接,要弄明确这个问题,须要先了解一下虚拟机是怎么执行泛型代码的。
       虚拟机的一种机制:擦除类型參数,并将其替换成特定类型,没有指定特定类型用Object取代,如前文中的Couple<T>类,虚拟机擦除后:   
[code01]

     public class Couple {
private Object wife ;
private Object husband ; public Couple(Object wife, Object husband) {
this.wife = wife;
this.husband = husband;
}
public void setWife(Object wife) {this. wife = wife;}
public void setHusband(Object husband) {this. husband = husband;} public Object getWife() {return wife;}
public Object getHusband() {return husband;}
}
     类型參数T是一个随意类型的,所以擦除后用Object取代了。无论是Couple<Employee>或者Couple<String>擦除后都成为了原始类Couple类,这就好比回到了泛型引入Java之前的普通类。所以这里重点环绕着擦除类型參数这个机制展开解说。
     如有对类型參数有类型限定会怎么替换呢?擦除类型參数机制告诉我们,使用限定的类型取代,假设有多个,使用第一个取代,看一段代码:
[code02]

     public class Period<T extends Comparable<T> & Serializable> {
private T begin;
private T end; public Period(T one, T two) {
if (one.compareTo(two) > 0) {begin = two;end = one;
} else {begin = one;end = two;}
}
}
     code02擦除后,Period的原始类型例如以下:
[code03]

     public class Period {
private Comparable begin;
private Comparable end; public Period(Comparable one, Comparable two) {
if (one.compareTo(two) > 0) {begin = two; end = one;
} else {begin = one; end = two;}
}
}
     思考一下,假设将Period<T extends Comparable<T> & Serializable>写成Period<T extends Serializable 
& Comparable<T>>会是怎么样呢?同理,擦除后原始类型用第一个Serializable取代,这样进行compareTo方法调用的时候,编译器会进行必要的强制类型转换,所以为了提高效率,将标签接口(没有不论什么方法的接口,也叫tagging接口)放在后面。
     先来看看虚拟机运行表达式的时候发生了什么,如:
[code04]

     Couple<Employee> couple = ...;
Employee wife = couple.getWife();
     擦除后,getWife()返回的是Object类型,然后虚拟机会插入强制类型转换,将Object转换为Employee,所以虚拟机实际上运行了两天指令:
     1.调用Couple.getWife()方法。
     2.将Object转换成Employee类型。
     再来看看虚拟机运行泛型方法的时候发生了什么,泛型方法如:
[code05]

public static <T extends Comparable<T>> max(T[] arrays) {... }
擦除后成了:
public staticComoparable max(Comparable[] arrays) {... }
     可是泛型方法的擦除会带来两个复杂的问题,且看第一个实例,一个实例:
[code06]

     public class Period <T extends Comparable<T> & Serializable> {
private T begin;
private T end; public Period(T one, T two) {
if (one.compareTo(two) > 0) {begin = two;end = one;
} else {begin = one;end = two;}
}
public void setBegin(T begin) {this. begin = begin;}
public void setEnd(T end) {this. end = end;}
public T getBegin() {return begin;}
public T getEnd() {return end;}
}
public class DateInterval extends Period<Date> { public DateInterval(Date one, Date two) {
super(one, two);
}
public void setBegin(Date begin) {
super.setBegin(begin);
}
}
     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<Date> period  = new DateInterval(...);
period.setBegin(new Date());
     这里由于period引用指向了DateInterval实例,依据多态性,setBegin应该调用DateInterval对象的setBegin方法,但是这个擦除让Period中的 public void setBegin(Object begin)
{...}被调用,导致了擦除与多态发生了冲突,怎么办呢?虚拟机此时会在DateInterval类中生成一个桥方法(bridge method),调用过程发生了细微的变化:
[code08]

     public void setBegin(Object begin) {
setBegin((Date)begin);
}
     有了这个合成的桥方法以后,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
public Date getBegin(){ return super.getBegin(); }
     这里用了@Override,说明是覆盖了父类的Object getBegin()方法,而返回值能够指定为父类中的返回值类型的子类,这就是协变类型,这是Java5以后才干够同意的,同意子类覆盖了方法后指定一个更严格的类型(子类型)。
总结:
     1.记住一点,虚拟机中没有泛型,仅仅有普通的类。
     2.全部泛型的类型參数都用它们限定的类型取代,没有限定则用Object。
     3.为了保持类型安全性,虚拟机在有必要时插入强制类型转换。
     4.桥方法的合成用来保持多态性。
     5.协变类型同意子类覆盖方法后返回一个更严格的类型。


=====【感谢亲阅读寻常心的文章,亲若认为此文有帮助,顶一顶亲的支持将给我前进的动力】=====

Java泛型解析(03):虚拟机运行泛型代码的更多相关文章

  1. Java泛型解析(01):认识泛型

    Java泛型解析(01):认识泛型 What      Java从1.0版本号到如今的8.中间Java5中发生了一个非常重要的变化,那就是泛型机制的引入.Java5引入了泛型,主要还是为了满足在199 ...

  2. Java:IDEA设置虚拟机运行时参数

    第一步:打开“Run->Edit Configurations”菜单 第二步:选择“VM Options”选项,输入你要设置的VM参数 第三步:点击“OK”.“Apply”后设置完成

  3. Java泛型解析(04):约束和局限性

    Java泛型解析(04):约束和局限性           前两节.认识和学习了泛型的限定以及通配符.刚開始学习的人可能须要一些时间去体会到泛型程序设计的优点和力量,特别是想成为库程序猿的同学就须要下 ...

  4. Java泛型解析(02):通配符限定

    Java泛型解析(02):通配符限定      考虑一个这种场景.计算数组中的最大元素. [code01] public class ArrayUtil { public static <T&g ...

  5. WCF 已知类型和泛型解析程序 KnownType

    数据协定继承 已知类型和泛型解析程序 Juval Lowy 下载代码示例 自首次发布以来,Windows Communication Foundation (WCF) 开发人员便必须处理数据协定继承方 ...

  6. java基础(9) - 泛型解析

    泛型 定义简单的泛型类 泛型方法 /** * 1.定义一个泛型类 * 在类名后添加类的泛型参数 <T> * 泛型类里面的所有T会根据创建泛型类时传入的参数确定类型 * 2.定义泛型方法 * ...

  7. 《徐徐道来话Java》(2):泛型和数组,以及Java是如何实现泛型的

    数组和泛型容器有什么区别 要区分数组和泛型容器的功能,这里先要理解三个概念:协变性(covariance).逆变性(contravariance)和无关性(invariant). 若类A是类B的子类, ...

  8. 从fastjson多层泛型嵌套解析,看jdk泛型推断

    给你一组json数据结构,你把它解析出来到项目中,你会怎么做? // data1 sample { "code" : "1", "msg" ...

  9. java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

随机推荐

  1. 【Android基础】listview控件的使用(4)-----自定义布局的listview的使用

    前面我介绍了listview控件的不同用法,但是这些用法在实际的开发项目中是不足以满足需求的,因为前面的几种用法只能简单的显示文本信息,而且布局都比较单一,很难做出复杂的结果,在实际的开发项目中,90 ...

  2. 2014 ACM湖南匹配10会议省赛

    2014湖南游戏..... 1:牡丹江Regional有些球队没来的冲突 2:题目比較水 3:队友神勇发挥 最终在开局不利的情况下完毕了翻盘,拿到了第二名.....没有抓住机会顺势夺冠还是非常遗憾的. ...

  3. MEF初体验之十:部件重组

    一些应用程序被设计成在运行时可以动态改变.例如,一个新的扩展被下载,或者因为其它的多种多样的原因其它的扩展变得不可用.MEF处理这些多样的场景是依赖我们称作重组的功能来实现的,它可已在最初的组合后改变 ...

  4. JUnit使用参数测试和一组测试

    JUnit该参数测试和一组测试使用简单 参数测试 作为替代阵列int a0,a1,a2喜欢,当测试加法assertEquals(3.0, h.add(1, 2), 0.1);相当于声明一个变量,要測试 ...

  5. Unity带有网络功能——创建服务,并连接到一个特定的服务

    游戏本身需要在网络上创建服务,然后其他的游戏能够连接到这个服务,此外真实场景现在玩同样的游戏效果一起. 该方法是创建一个服务呼叫Network.InitializeServer( ): 是Networ ...

  6. linux 字符界面浏览器 w3m(转)

    最近找到了几个linux终端下使用的浏览器,主要用来测试本机web服务器是否搭建成功.因为我们一般是用ssh客户端连接linux的,所以很需要一个字符界面的浏览器.找了几个显示都不理想,只有w3m用起 ...

  7. 关于TCP/IP协议栈(转)

    1. TCP/IP协议栈 与OSI参考模型不同,TCP/IP协议栈共有4层,其中网络接口层对应OSI中的物理层和数据链路层,应用层对应OSI中的应用层.表示层和会话层. 在网络接口层的主要协议有:AR ...

  8. 一个关于Linux Bridge配置的吐嘈

    话说有些事情十分适合在放假前的一天折腾一天,但绝对不适合在你准备去吃饭前多看一眼...我上周就碰到了这么一件揪心的事,终于以低血糖收场.十分狼狈地四处觅食,却觅到了一包超级辣的鸡爪.吃完后感觉症状加重 ...

  9. iOS 生成随机颜色(UIColor)

    #import <UIKit/UIKit.h> @interface UIColor (RandomColor) +(UIColor *) randomColor; @end #impor ...

  10. 【git学习五】git基础之git分支

    1.背景                最早用github的时候,我傻傻的问舍友大神,git里面的branch是干什么的,他用了非常直白的解释,我至今还记得."branch就是你能够自己建立 ...