美文网首页
JAVA 泛型意淫之旅(二)

JAVA 泛型意淫之旅(二)

作者: 95a6af369245 | 来源:发表于2019-02-13 17:17 被阅读108次

编译器如何处理泛型

泛型类编译后长什么样?

接上文,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 时,进行了类型擦除,即删除了参数类型,我们反编译下 MakeBaby 类

从反编译结果可以看出,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);

}

总结:

当泛型类的泛型变量没有类型限制时,类型擦除后所有的 T 被替换为 Object;

当泛型类的类型变量有一个限定类型时,类型擦除后所有的 T 不再被替换为 Object,而是替换为限定的类型;

当泛型类的类型变量有多个限定类型时,类型擦除后所有的 T 被替换为第一个限定的类型。

为了保证多态性,编译时会生成桥方法;

桥方法接收的参数为 Object 类型,为了保证类型的安全性,会进行强制类型转换;

类型擦除发生在编译时,虚拟机中没有泛型,只有普通的类和方法。

相关文章

  • JAVA 泛型意淫之旅(二)

    编译器如何处理泛型 泛型类编译后长什么样? 接上文,JAVA 泛型意淫之旅1,成功迎娶白富美后,终于迎来了最振奋人...

  • Java泛型教程

    Java泛型教程导航 Java 泛型概述 Java泛型环境设置 Java泛型通用类 Java泛型类型参数命名约定 ...

  • Java泛型(黑马程序员武汉中心)

    Java泛型 一、概述 1、泛型含义 2、泛型好处 3、泛型分类 二、常见的泛型案例 1、泛型方法 A.定义时 B...

  • 第二十八课:泛型

    泛型出现之前 泛型出现之后 Java深度历险(五)——Java泛型

  • 想理解泛型吗?看这一篇就够了!

    一、前言二、泛型类2.1 概述Java中泛型使用情况大致包括三种:泛型类、泛型接口、泛型方法 本节演示泛型类。 2...

  • Kotlin 泛型

    说起 kotlin 的泛型,就离不开 java 的泛型,首先来看下 java 的泛型,当然比较熟悉 java 泛型...

  • Java一泛型

    目录 一、设计背景 二、什么是泛型?泛型的作用? 三、泛型的原理 四、使用泛型 一、设计背景 Java集合(Col...

  • java泛型中类型擦除的一些思考

    java泛型 java泛型介绍 java泛型的参数只可以代表类,不能代表个别对象。由于java泛型的类型参数之实际...

  • Java泛型

    参考:Java知识点总结(Java泛型) 自定义泛型类 自定义泛型接口 非泛型类中定义泛型方法 继承泛型类 通配符...

  • Java泛型—Java语法糖,只在编译有作用,编译后擦出泛型

    Java泛型—Java语法糖,只在编译有作用,编译后擦出泛型 在代码进入和离开的边界处,会处理泛型 Java泛型作...

网友评论

      本文标题:JAVA 泛型意淫之旅(二)

      本文链接:https://www.haomeiwen.com/subject/qxideqtx.html