Java语法糖系列三:泛型与类型擦除

目录:
Java语法糖系列一:可变长度参数和foreach循环
//www.greatytc.com/p/628568f94ef8

Java语法糖系列二:自动装箱/拆箱和条件编译
//www.greatytc.com/p/946b3c4a5db6

Java语法糖系列三:泛型与类型擦除
//www.greatytc.com/p/4de08deb6ba4

Java语法糖系列四:枚举类型
//www.greatytc.com/p/ae09363fe734

Java语法糖系列五:内部类和闭包
//www.greatytc.com/p/f55b11a4cec2


前两篇写到可变长参数、foreach循环、自动装箱/拆箱和条件编译,这篇讨论下java的泛型与类型擦除。

泛型与类型擦除

泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。

泛型思想早在C++语言的模板(Templates)中就开始生根发芽,在Java语言处于还没有出现泛型的版本时,只能通过Object是所有类型的父类和类型强制转换两个特点的配合来实现类型泛化。

例如在哈希表的存取中,JDK 1.5之前使用HashMap的get()方法,返回值就是一个Object对象,由于Java语言里面所有的类型都继承于java.lang.Object,那Object转型成任何对象都是有可能的。但是也因为有无限的可能性,就只有程序员和运行期的虚拟机才知道这个Object到底是个什么类型的对象。在编译期间,编译器无法检查这个Object的强制转型是否成功,如果仅仅依赖程序员去保障这项操作的正确性,许多ClassCastException的风险就会被转嫁到程序运行期之中。

泛型技术在C#和Java之中的使用方式看似相同,但实现上却有着根本性的分歧,C#里面泛型无论在程序源码中、编译后的IL中(Intermediate Language,中间语言,这时候泛型是一个占位符)或是运行期的CLR中都是切实存在的,List<int>与List<String>就是两个不同的类型,它们在系统运行期生成,有自己的虚方法表和类型数据,这种实现称为类型膨胀,基于这种方法实现的泛型被称为真实泛型

Java语言中的泛型则不一样,它只在程序源码中存在,在编译后的字节码文件中,就已经被替换为原来的原生类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转型代码。
先看个例子

public static void main(String[] args){
 List <Integer> listInt=new ArrayList <Integer>();
 List <String> listString=new ArrayList <String>();

 Map<String, String> map = new HashMap<String, String>();  
    map.put("AAA", "BBB");  
    map.put("CCC", "DDD");  
    System.out.print(map.get("AAA"));
}

编译出来的代码

public static void main(String[] paramArrayOfString)
   {
    ArrayList localArrayList1 = new ArrayList();
    ArrayList localArrayList2 = new ArrayList();

        HashMap localHashMap = new HashMap();
    localHashMap.put("AAA", "BBB");
    localHashMap.put("CCC", "DDD");
    System.out.print((String)localHashMap.get("AAA"));
  }

因此对于运行期的Java语言来说,ArrayList <Integer>与ArrayList<String>编译出来的代码是一样的,所以说泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型被称为伪泛型。

泛型与重载

首先看看重载(Overloading)的定义:

  • Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
    
  • 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。
    
  • 无法以返回型别作为重载函数的区分标准。
    

上面也说了,泛型编译出来的代码是会把类型擦除的,所以如下的代码是不能编译的,是因为参数List<Integer>和List<String>编译之后都被擦除了,变成了一样的原生类型List<E>,擦除动作导致这两个方法的特征签名变得一模一样,或者说两个一模一样的方法不能共存在一个class文件里。

   public  void method(List<String> list) {  
      System.out.println(list.get(0));  
}  

public  void method(List<Integer> list) {  
    System.out.println(list.get(0));  
}  

那么如果加上返回类型呢?

public String method(List<String> list) {  
    System.out.println(list.get(0));  
    return "";  
}  

public int method(List<Integer> list) {  
    System.out.println(list.get(0));  
    return 1;  
}  

在eclipse 上仍然报错,参考重载定义的第三点无法以返回型别作为重载函数的区分标准

一点拓展

上面加上了返回类型的两个方法,在eclipse上编译不通过,但在Javac编译器中是可以编译的,编译出来的代码如下:

public String method(List<String> paramList)
  {
    System.out.println((String)paramList.get(0));
    return "";
  }

  public int method(List<Integer> paramList) {
        System.out.println(paramList.get(0));
        return 1;
  }

那是不是说明重载可以以返回类型作区分呢?不是的。因为像以下这样的代码用javac也编译不了

`public String method(String list) {  
    System.out.println(list);  
    return "";  
}  

public int method(String list) {  
    System.out.println(list);  
    return 1;  
}  `

网上找到一段引用:

在《Java虚拟机规范第二版》(JDK 1.5修改后的版本)的“§4.4.4 Signatures”章节及《Java语言规范第三版》的“§8.4.2 Method Signature”章节中分别都定义了字节码层面的方法特征签名,以及Java代码层面的方法特征签名,特征签名最重要的任务就是作为方法独一无二不可重复的ID,在Java代码中的方法特征签名只包括了方法名称、参数顺序及参数类型,而在字节码中的特征签名还包括方法返回值及受查异常表。

根据上面的例子说明:由于List<String>和List<Integer>擦除后是同一个类型,只能添加两个并不需要实际使用到的返回值才能完成重载。这是否是一种引入泛型后的折中的解决方案呢?

最后,通过反射依然能获取到参数化的类型,说明擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息。
测试如图:

反射获取参数化类型.jpg

参考:http://icyfenix.iteye.com/blog/1021949?page=2#comments

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容

  • 前面,由于对泛型擦除的思考,引出了对Java-Type体系的学习。本篇,就让我们继续对“泛型”进行研究: JDK1...
    贾博岩阅读 5,139评论 3 28
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,166评论 9 118
  • 开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误。比如一个方法如果接收List作为形式参数,那么如果尝试...
    时待吾阅读 1,045评论 0 3
  • UIWebView 一、概览(Overview) 1.支持的文件格式(Supported File Formats...
    ShannonChenCHN阅读 1,272评论 0 1
  • 从小一起长大的菇凉明天要做别人的新娘了,替她开心,即使婚前有纠结,有烦恼,既然选择了,就要嫁给幸福~ 很...
    傻丫头的西瓜阅读 196评论 0 1