点击上方“IT烂笔头”,选择“置顶公众号”javascript
第一时间获取 IT 技术干货!java
阅读文本大概须要 6 分钟。android
1web
Java是怎么实现泛型的?不错,类型擦除。Java编译器将源码编译成字节码的时候会将你在源码中声明的类型进行擦除,好比:微信
List<String> list = new ArrayList<String>();
在编译后,字节码中只有List,运行在JVM中也是同样的,那你可能会有疑问,既然将类型擦除了,那为何我声明的泛型为String类型时,不能往里add一个整型的数据呢?这是由于编译器在编译前会进行类型检查,类型不一致会直接编译报错。
通常做为初级工程师知道这些就算合格了。app
咱们往深一层研究下,难道咱们必定不能往声明泛型为String的list中增长一个整型元素吗?聪明的同窗可能想到了,既然是在编译前检查类型的,编译后又将类型擦除了,那我是否是能够在运行时经过反射将整型数字add进去?不错!确实是能够的。编辑器
List<Long> list = new ArrayList<Long>();list.add(100L);list.getClass().getMethod("add", Object.class).invoke(list, "abc");
// 而后咱们将list打印出来for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i));}
打印出来确实是能够的,可是我能改为下面这样吗?ui
List<Long> list = new ArrayList<Long>();list.add(100L);list.getClass().getMethod("add", Object.class).invoke(list, "abc");
Long a = 0L;// 而后咱们将list打印出来for (int i = 0; i < list.size(); i++) { a += list.get(i);}
固然不能啦,固然若是你想的比较仔细,会发现一个很好玩的现象。对于上面这个list中第二个元素为"abc",那咱们分别以不一样的类型来赋值:url
String s = "";Long a = 0L;s = list.get(1);a = list.get(1);
不管你是用String仍是Long类型来接收都会报错,你用String来接收会报"Long cannot be converted to String"。你用Long类型来赋值会报"String cannot be cast to java.lang.Long",你是否是感到迷茫了,其实这一切都是由于类型擦除,对于不能使用String类型来接收是由于编译器会作检查,由于list声明的泛型是Long类型的,而你使用String类型来赋值显然编译器会报错,第二种你使用Long类型来接收,编译器固然会认为是合法的,可是在运行的时候,list中的第二个实际值是String,强转成Long固然会报错了。这一切的根源是你使用反射向list中放入了一个和声明不一样类型的数据。正常咱们通常也不会这么作啦,这里只是验证一下这个机制而已。spa
好了,解释了这么多类型擦除的机制,那Java使用类型擦除来实现泛型有什么好处呢?
一、第一点咱们将如此多的泛型在编译时擦除了,那么在运行时显然能够省很多的内存空间嘛。
二、第二点不得不说下兼容性,Java是在1.5版本推出的泛型,那1.5以前存在大量的线上代码没有泛型的,总不能舍弃吧,因此编译擦除后和没有泛型不是同样吗,这就兼容了以前更老的Java版本。
若是到这里你基本上都会的话,我以为彻底具备中级工程师的能力了。
2
类 型 擦 除 带 来 的 问 题
任何设计都会有本身的优势和缺点,在了解类型擦除的优势以后,咱们也要剖析下类型擦除存在的现实问题:
一、不能使用基本数据类型
对于基本数据类型咱们必须使用它的装箱类,那在咱们使用过程当中必然会平凡的涉及到拆箱和装箱的操做,这一定带来必定的资源开销,因此谷歌在针对key是int类型的状况下,使用SparseArray来代替HashMap。
二、不能用来方法的重载
为何呢?举个例子:
如上图所示,在不一样的泛型做为参数时,编译器编译时进行类型擦除,那参数不就同样了吗?那还谈什么重载呢!而C#没有进行类型擦除,因此编译完后是带有泛型的类型的,因此能够看成是重载的。
三、泛型类型不能看成真实的类型使用
上图中展现了5种使用方式,除了第四种Java能正常使用,其余Java都不能使用,而C#彻底没问题。
四、静态方法没法引用类的泛型类型
Java中的泛型是类实例化的时候才能肯定泛型的准确类型,而静态方法是不须要类实例化就能调用的,显然不能使用。
五、类型强转的开销
在Java1.5以前的版本,如上图所示,必需要进行强转才能使用本身想要的类型。
那Java1.5及之后的版本呢?
有兴趣的能够看看ArrayList的源码,它的get方法仍是会作强转的。
若是到这里你都知道,说明你对泛型以及类型擦除了解的仍是比较系统,彻底具有高工的潜质。
推荐阅读:
THANDKS
- End -
本文分享自微信公众号 - IT烂笔头(nj_android)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。