JAVA 泛型意淫之旅(二)
编译器如何处理泛型
泛型类编译后长什么样?
接上文,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); }
总结:
- 当泛型类的泛型变量没有类型限制时,类型擦除后所有的 T 被替换为 Object;
- 当泛型类的类型变量有一个限定类型时,类型擦除后所有的 T 不再被替换为 Object,而是替换为限定的类型;
- 当泛型类的类型变量有多个限定类型时,类型擦除后所有的 T 被替换为第一个限定的类型。
- 为了保证多态性,编译时会生成桥方法;
- 桥方法接收的参数为 Object 类型,为了保证类型的安全性,会进行强制类型转换;
- 类型擦除发生在编译时,虚拟机中没有泛型,只有普通的类和方法。
JAVA 泛型意淫之旅(二)的更多相关文章
- Java学习笔记(二一)——Java 泛型
[前面的话] 最近脸好干,掉皮,需要买点化妆品了. Java泛型好好学习一下. [定义] 一.泛型的定义主要有以下两种: 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个 ...
- 解析java泛型(二)
上篇我们简单的介绍了java中泛型的最基本的内容,知道了什么是泛型以及泛型对我们的程序编写有什么好处,最后一类型限定收尾.本篇将从类型限定开始阐述java泛型中很重要的概念:通配符 一.何为通配符 ...
- 大白话说Java泛型(二):深入理解通配符
文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java泛型(二):深入理解通配符> 上篇文章<大白话说Java泛型(一):入门.原理.使用>,我们讲了泛型的产生缘由以及 ...
- java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题
微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...
- Java基础系列二:Java泛型
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 一.泛型概述 1.定 ...
- Java泛型反射机制(二)
/** * @author Administrator * 好处:泛型:1安全 2减少代码重用率 */ package com.test; import java.lang.reflect.Metho ...
- 赢在面试之Java泛型篇(十二)
139. Java中的泛型是什么 ? 使用泛型的好处是什么? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 好处: 1.类型安全,提供编译期 ...
- java基础(十二 )-----Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...
- Java泛型读书笔记 (二)
关于Java泛型擦除后,继承一个泛型类带来的问题 有如下泛型类Pair: public class Pair<T> { private T second; private T first; ...
随机推荐
- 复制新项目 ,tomcat部署时名字还是旧项目名
基于一个就项目 copy 成新项目 关于项目名注意点: 在工作空间下 copy一份新项目 1首先在目录将项目名字更改. 2.在新项目下 找到 [.project]文件 将里面的nama更改 3 ...
- Group By 与 Count
select UserID,COUNT(0) From [Order] a Group By UserID UserID (无列名)1 5 2 ...
- Spring MVC http请求地址映射(三)
Spring MVC框架通过扫描将带有@Controller的类中的@RequestMapping的方法进行映射,然后调用映射的方法处理请求,这个分发过程默认是由DispaterServlet处理的. ...
- 配置YARN
1.配置yarn-site.xml(所有节点) 路径: /usr/local/hadoop-2.7.3/etc/hadoop/yarn-site.xml 配置项: <property> & ...
- Python3+Selenium3自动化测试-(三)
selenium键盘事件 #coding=utf-8 from selenium import webdriver import time from selenium.webdriver.common ...
- Java基础—枚举
定义 枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示. 为什么要用枚举 在java语言中还没有引入枚举类型之前,表示枚 ...
- Spring 数据库连接池读取系统环境变量作为参数
原来是写在一个properties文件里面,后来项目要部署的的确太多了,每次更改不太方便,就想把这些固定不变的信息写在当地的环境变量里面 原先是这样的:引用的所有信息在jdbc.properties ...
- JFinal项目搭建
1.Myeclipse中 新建 Dynamic Web Project 导入jar包 2.配置web.xml <?xml version="1.0" encoding=& ...
- 解释一下python中的逻辑运算符
python中有三个逻辑运算符:and.or.not print(False and True)#False print(7<7 or True)#True print(not 2==2)#Fa ...
- NSIS卸载后无法删除开始菜单中的内容
我们在安装程序时通常会使用createShortCut命令来创建一个快捷方式,如下,是在开始–>启动项 里创建run.bat的快捷方式. CreateShortCut "$SMPROG ...