Java泛型之类型擦除类型擦除参考资料

本文首发于个人网站:Java阿杜

类型擦除

学过C++模板的,在使用Java泛型的时候,会感觉到有点不疑问,例如:(1)无法定义一个泛型数组、无法调用泛型参数对象中对应的方法(当然,通过extends关键字是可以做到,只是比较麻烦);(2)ArrayList<Integer>和ArrayList<String>在运行时的类型是相同的。Java中的泛型有这些问题,是它的实现机制决定的,即“类型擦除”。

  1. 类型擦除的定义:编译通过后,准备进入JVM运行时,就不再有类型参数的概念,换句话说:每定义一个泛型类型,JVM会自动提供一个对应的原生类; public class Holder4<T> { private T a; private T b; private T c; public Holder4(T a, T b, T c) { this.a = a; this.b = b; this.c = c; } public T getA() { return a; } public T getB() { return b; } public T getC() { return c; } public void setA(T a) { this.a = a; } public void setB(T b) { this.b = b; } public void setC(T c) { this.c = c; } public static void main(String[] args) { Holder4<Automobile> holder4 = new Holder4<>(new Automobile(),new Automobile(), new Automobile()); Automobile a = holder4.getA(); //编译器帮忙转型,不需要显式转型 Automobile b = holder4.getB(); Automobile c = holder4.getC(); } } 在Java中,每定义一个泛型类型,就会自动提供一个对应的原始类型,例如: public class Holder4Raw { private Object a; private Object b; private Object c; public Holder4Raw(Object a, Object b, Object c) { this.a = a; this.b = b; this.c = c; } public Object getA() { return a; } public Object getB() { return b; } public Object getC() { return c; } public void setA(Object a) { this.a = a; } public void setB(Object b) { this.b = b; } public void setC(Object c) { this.c = c; } public static void main(String[] args) { Holder4Raw holder4Raw = new Holder4Raw(new Automobile(),new Automobile(), new Automobile()); Automobile a = (Automobile) holder4Raw.getA(); //显示的转型 Automobile b = (Automobile) holder4Raw.getB(); Automobile c = (Automobile) holder4Raw.getC(); } }
  2. 为什么选择这种实现机制?
    • 在Java诞生10年后,才想实现类似于C++模板的概念,即泛型;
    • Java的类库是Java生态中非常宝贵的财富,必须保证向后兼容(即现有的代码和类文件依旧合法)和迁移兼容(泛化的代码和非泛化的代码可互相调用)基于上面这两个背景和考虑,Java设计者采取了“类型擦除”这种折中的实现方式。
  3. Java泛型依赖编译器实现,只存在于编译期,JVM中没有泛型的概念;那么,编译器做了什么工作呢?(1)set方法是编译期检查;(2)get方法的返回值进行转型,编译器插入了一个checkcast语句。 我们通过字节码进行观察,可以看出:(1)Holder4和Holder4Raw两个类的字节码完全相同;(2)在main函数的33、41和49行就是编译器插入的checkcast语句; public class org.java.learn.generics.Holder4<T> { public org.java.learn.generics.Holder4(T, T, T); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: aload_1 6: putfield #2 // Field a:Ljava/lang/Object; 9: aload_0 10: aload_2 11: putfield #3 // Field b:Ljava/lang/Object; 14: aload_0 15: aload_3 16: putfield #4 // Field c:Ljava/lang/Object; 19: return public T getA(); Code: 0: aload_0 1: getfield #2 // Field a:Ljava/lang/Object; 4: areturn public T getB(); Code: 0: aload_0 1: getfield #3 // Field b:Ljava/lang/Object; 4: areturn public T getC(); Code: 0: aload_0 1: getfield #4 // Field c:Ljava/lang/Object; 4: areturn public void setA(T); Code: 0: aload_0 1: aload_1 2: putfield #2 // Field a:Ljava/lang/Object; 5: return public void setB(T); Code: 0: aload_0 1: aload_1 2: putfield #3 // Field b:Ljava/lang/Object; 5: return public void setC(T); Code: 0: aload_0 1: aload_1 2: putfield #4 // Field c:Ljava/lang/Object; 5: return public static void main(java.lang.String[]); Code: 0: new #5 // class org/java/learn/generics/Holder4 3: dup 4: new #6 // class org/java/learn/generics/Automobile 7: dup 8: invokespecial #7 // Method org/java/learn/generics/Automobile."<init>":()V 11: new #6 // class org/java/learn/generics/Automobile 14: dup 15: invokespecial #7 // Method org/java/learn/generics/Automobile."<init>":()V 18: new #6 // class org/java/learn/generics/Automobile 21: dup 22: invokespecial #7 // Method org/java/learn/generics/Automobile."<init>":()V 25: invokespecial #8 // Method "<init>":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V 28: astore_1 29: aload_1 30: invokevirtual #9 // Method getA:()Ljava/lang/Object; 33: checkcast #6 // class org/java/learn/generics/Automobile,get方法的转型 36: astore_2 37: aload_1 38: invokevirtual #10 // Method getB:()Ljava/lang/Object; 41: checkcast #6 // class org/java/learn/generics/Automobile,get方法的转型 44: astore_3 45: aload_1 46: invokevirtual #11 // Method getC:()Ljava/lang/Object; 49: checkcast #6 // class org/java/learn/generics/Automobile,get方法的转型 52: astore 4 54: return }

参考资料

  1. 《Java编程思想》
  2. 《Effective Java》
  3. 《Java核心技术》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏蓝天

rapidjson常见使用示例

Document d; v2.CopyFrom(d, a); // 把整个document复制至v2,d不变 rapidjson为了最大...

1.1K2
来自专栏技术博客

C#基础知识系列一(goto、i++、三元运算符、ref和out、String和string、重载运算符)

  这两天在网上看到的总结很多,尤其是博客园中的,很多很多,也给了我很多的启发,当然自己也总结过,而且有很多人也给与我一些意见和看法。不管怎样,自己还是先把所谓...

1262
来自专栏Java架构

Java 8简明教程

2385
来自专栏向治洪

Java泛型type体系

最近看开源代码,看到里面很多Java泛型,并且通过反射去获取泛型信息。如果说要看懂泛型代码,那还是比较容易,但是如果要自己利用泛型写成漂亮巧妙的框架,那必须对...

2228
来自专栏一枝花算不算浪漫

[读书笔记]C#学习笔记八:StringBuilder与String详解及参数传递问题剖析

37214
来自专栏blackheart的专栏

[C#3] 1-扩展方法

1.从DEMO开始 先看一个扩展方法的例子: 1 class Program 2 { 3 public static void Main(...

21910
来自专栏GreenLeaves

C# 通过IEnumberable接口和IEnumerator接口实现自定义集合类型foreach功能

1、IEnumerator和IEnumerable的作用 其实IEnumerator和IEnumerable的作用很简单,就是让除数组和集合之外的类型也能支持f...

20710
来自专栏海说

17、Map接口及其常用子类(Hashtable、HashMap、WeakHashMap)

17、Map接口   Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个val...

2090
来自专栏Ryan Miao

Java8学习(3)- Lambda 表达式

猪脚:以下内容参考《Java 8 in Action》 本次学习内容: Lambda 基本模式 环绕执行模式 函数式接口,类型推断 方法引用 Lambda 复...

3359
来自专栏大内老A

从yield关键字看IEnumerable和Collection的区别

C#的yield关键字由来以久,如果我没有记错的话,应该是在C# 2.0中被引入的。相信大家此关键字的用法已经了然于胸,很多人也了解yield背后的“延迟赋值”...

2247

扫码关注云+社区

领取腾讯云代金券