编译器如何处理泛型

泛型类编译后长什么样?

接上文,JAVA 泛型意淫之旅1,成功迎娶白富美后,终于迎来了最振奋人心的一刻:造娃!造男娃还是造女娃?对于我们程序猿来说,谁还在乎是男娃女娃,只要是自己的娃,就是好娃!但不知道父母们是不是这么想的,我们先搞一个造娃类,问一问造出不同的娃,父母的态度是什么。

public class MakeBaby<T> {
    private T baby;

    public T getBaby() {
        return baby;
    }

    public void setBaby(T baby) {
        this.baby = baby;
    }
}

先造两个试一下

MakeBaby<Boy> boy = new MakeBaby<>();
MakeBaby<Girl> girl = new MakeBaby<>();
System.out.println("\"男娃女娃都行吗?\"" + "\"" + (boy.getClass() == girl.getClass()) + "\"");

造完了我们看下父母态度

为什么男娃女娃都一样呢?其实是编译器在编译 MakeBaby<T> 时,进行了类型擦除,即删除了参数类型,我们反编译下 MakeBaby<T> 类


从反编译结果可以看出,getBaby 返回的是 Object 类型,setBaby 赋值的也是 Object 类型,类型变量被擦除掉了。编译器编译后实际交付给 JVM 的是

public class MakeBaby {
    private Object baby;

    public Object getBaby() {
        return baby;
    }

    public void setBaby(Object baby) {
        this.baby = baby;
    }
}

所以无论是 MakeBaby<Boy> 还是 MakeBaby<Girl>, 最终生成的代码都是 MakeBaby.

如果有类型限定,擦除后会是什么样呢?和女神造娃怎么能只造出一个普通的娃呢,这娃以后必须得会撩妹!稍微修改一下 MakeBaby<T> 类,添加一个类型限定

public class MakeBaby<T extends PickUpGirl> {
    private T baby;

    public T getBaby() {
        return baby;
    }

    public void setBaby(T baby) {
        this.baby = baby;
    }
}

再来看一下生成的结果


getBaby 和 setBaby 的类型不再是 Object, 而是我们限定的 PickUpGirl 了。如果有多个类型限定会怎样呢?我们不但要保证娃以后会撩妹,还要能赚钱,算是对孩子的美好祝福吧。

public class MakeBaby<T extends PickUpGirl & MakeMoney> {
    ...
}

看一下生成的结果


为什么生成的类型不是 MakeMoney 而是 PickUpGirl 呢?我们把两个接口位置替换一下

public class MakeBaby<T extends MakeMoney & PickUpGirl> {
    ...
}

再来看一下


生成的类型变成了 MakeMoney.

泛型方法编译后长什么样?

普通的泛型方法类型擦除我们不再讨论,和上面的泛型类类型擦除规则相同,主要讨论下泛型方法在多态情况下的类型擦除。

假设我们生了一个男孩儿,男孩儿遗传了伟大父亲的众多基因,我们姑且先以遗传了父亲的相貌为例。

// 父亲类
public class Father<T> {
    private T majorFeature;

    public Father(){
        this.setMajorFeature(null);
    }

    public Father(T feature){
        this.setMajorFeature(feature);
    }

    public T getMajorFeature() {
        return majorFeature;
    }

    public void setMajorFeature(T majorFeature) {
        this.majorFeature = majorFeature;
    }
}

// 外表类
public class Appearance {
    private int FeaturesScore;

    public Appearance(int featuresScore){
        this.setFeaturesScore(featuresScore);
    }

    public int getFeaturesScore() {
        return FeaturesScore;
    }

    public void setFeaturesScore(int featuresScore) {
        FeaturesScore = featuresScore;
    }
}

// 孩子类
public class Boy extends Father<Appearance> {
    public void setMajorFeature(Appearance appearance) {
        if(appearance.getFeaturesScore() >= 6){
            super.setMajorFeature(appearance);
        }
    }
}

Father<Appearance> boy = new Boy();
// 调用的是 Boy 类的 setMajorFeature 方法,而不是 Father 类的 setMajorFeature
boy.setMajorFeature(new Appearance(8));

如果父亲颜值小于 6 分,还是任娃自由生长吧,如果父亲颜值大于 6 分,娃可以遗传一下父亲的帅气基因。

我们看下 Boy 类生成的了什么样的代码


从图中可以看到,生成了两个 setMajorFeature 方法,一个参数类型为 Appearance, 一个参数类型为 Object. 参数为 Object 类型的 setMajorFeature 方法被称之为桥方法

boy 变量声明为 Father<Appearance> 类型,这个类型有一个 setMajorFeature(T majorFeature) 方法,类型擦除后为 setMajorFeature(Object majorFeature). 虚拟机用 boy 引用的对象访问这个方法,boy 引用的对象为一个 Boy 类型的实例,由于多态性,它会调用 Boy.setMajorFeature(Object majorFeature) 方法,即上图生成的桥方法。看下桥方法做了什么操作,


由图可知,首先桥方法将变量进行了强制类型转换,转换为了 Appearance 类型,接着又调用了 setMajorFeature(Appearance majorFeature) 方法。这就是我们想要的结果,boy.setMajorFeature 调用了最合适的方法。实际生成的桥方法为

public void setMajorFeature(Object appearance){
    setMajorFeature((Appearance)appearance);
}

总结:

  1. 当泛型类的泛型变量没有类型限制时,类型擦除后所有的 T 被替换为 Object;
  2. 当泛型类的类型变量有一个限定类型时,类型擦除后所有的 T 不再被替换为 Object,而是替换为限定的类型;
  3. 当泛型类的类型变量有多个限定类型时,类型擦除后所有的 T 被替换为第一个限定的类型。
  4. 为了保证多态性,编译时会生成桥方法;
  5. 桥方法接收的参数为 Object 类型,为了保证类型的安全性,会进行强制类型转换;
  6. 类型擦除发生在编译时,虚拟机中没有泛型,只有普通的类和方法。

JAVA 泛型意淫之旅(二)的更多相关文章

  1. Java学习笔记(二一)——Java 泛型

    [前面的话] 最近脸好干,掉皮,需要买点化妆品了. Java泛型好好学习一下. [定义] 一.泛型的定义主要有以下两种: 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个 ...

  2. 解析java泛型(二)

    上篇我们简单的介绍了java中泛型的最基本的内容,知道了什么是泛型以及泛型对我们的程序编写有什么好处,最后一类型限定收尾.本篇将从类型限定开始阐述java泛型中很重要的概念:通配符 一.何为通配符   ...

  3. 大白话说Java泛型(二):深入理解通配符

    文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java泛型(二):深入理解通配符> 上篇文章<大白话说Java泛型(一):入门.原理.使用>,我们讲了泛型的产生缘由以及 ...

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

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

  5. Java基础系列二:Java泛型

    该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 一.泛型概述 1.定 ...

  6. Java泛型反射机制(二)

    /** * @author Administrator * 好处:泛型:1安全 2减少代码重用率 */ package com.test; import java.lang.reflect.Metho ...

  7. 赢在面试之Java泛型篇(十二)

    139. Java中的泛型是什么 ? 使用泛型的好处是什么? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 好处: 1.类型安全,提供编译期 ...

  8. java基础(十二 )-----Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...

  9. Java泛型读书笔记 (二)

    关于Java泛型擦除后,继承一个泛型类带来的问题 有如下泛型类Pair: public class Pair<T> { private T second; private T first; ...

随机推荐

  1. HDU 1863 畅通工程(Kruskal)

    畅通工程 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  2. delphi------项目类型

    Console Application:控制台应用程序 writeln('HelloWorld'); //接收用户输入字符 readln: //直到用户输入回车结束 VCL Forms Applica ...

  3. QQ视频直播架构及原理 流畅与低延迟之间做平衡 音画如何做同步?

    QQ视频直播架构及原理 - tianyu的专栏 - CSDN博客 https://blog.csdn.net/wishfly/article/details/53035342 作者:王宇(腾讯音视频高 ...

  4. C#中Windows通用的回车转Tab方法

    标签: windowsc#textboxbuttondropdownobject 2007-03-28 09:37 2773人阅读 评论(0) 收藏 举报  分类: C#(5)  版权声明:本文为博主 ...

  5. JavaBean 介绍

    // Person.java public class Person{ private String name; private int age; // 无参构造函数 public Person(){ ...

  6. numpy.random.random & numpy.ndarray.astype & numpy.arange

    今天看到这样一句代码: xb = np.random.random((nb, d)).astype('float32') #创建一个二维随机数矩阵(nb行d列) xb[:, 0] += np.aran ...

  7. 我的Android进阶之旅------>Android自定义View实现带数字的进度条(NumberProgressBar)

    今天在Github上面看到一个来自于 daimajia所写的关于Android自定义View实现带数字的进度条(NumberProgressBar)的精彩案例,在这里分享给大家一起来学习学习!同时感谢 ...

  8. JPA 对象关系映射之关联关系映射策略

    关联关系映射 关联关系映射,是映射关系中比较复杂的一种映射关系,总的说来有一对一.一对多和多对多几种关系.细分起来他们又有单向和双向之分.下面我们逐一介绍一下. 回页首 单向 OneToOne 单向一 ...

  9. Open Source VOIP applications, both clients and servers (开源sip server & sip client 和开发库)

    SIP Proxies SBO SIP Proxy Bypass All types of Internet Firewall JAIN-SIP Proxy Mini-SIP-Proxy A very ...

  10. Python之字符编码(Day10)

    1. python解释器执行py文件的原理 ,例如python test.py    第一阶段:python解释器启动,此时就相当于启动了一个文本编辑器 第二阶段:python解释器相当于文本编辑器, ...