Java 泛型系列四:虚拟机是如何实现泛型的?

173 阅读2分钟

Java 泛型系列四:虚拟机是如何实现泛型的?

本文概述:

  • 文章从底层实现原理上剖析了,泛型在 Java 中的实现;全文围绕泛型擦除展开,介绍了不同泛型编译前后的代码,并介绍了相关工具,同时补充了字节码细节,进一步说明了 Java 中的泛型,好像是伪泛型,哈哈哈;

虚拟机是如何实现泛型的:泛型擦除

  • 概述:

    • Java 虚拟机依托泛型擦除实现泛型,而C# 实现的是真真正正的泛型;
    • JDK 其中实际上是伪泛型;
  • 泛型擦除:编译后的效果

    • 擦除效果:

      • 原程序

         public class A<T>{
             private T data;
         }
        
      • 擦除后:

         public class A<Object>{
             private Object data;
         }
        
      • 复杂程序:指定了上界

         public class A <T extends ArrayList & 接口B>{
             private T data;
             
             public void test(){
                 data.接口B;
             }
         }
        
      • 擦除后:

         //对于这种多实现的,编译期会将第一限制作为擦除后的类型,对于后面的,如果在某个方法中调用了后面的接口,那么编译器会在合适的位置插入强制转型的代码
         public class A <ArrayList>{
             private ArrayList data;
             
             //重点在强制转型
             public void test(){
                 (接口B)data.接口B;
             }
         }
        
  • 如何证明 JDK 中实际上是伪泛型:使用工具 jdui

    • 工具展示:

      image-20220812002518781

    • 源代码:

      image-20220812002413280

    • 擦除后的效果:做了强制转型的

      image-20220812002726122

    • 细节:有些泛型是看不到的

      • 因为工具很强大,对于不同的代码的字节码呈现效果不同;
  • 编程时的冲突问题:

    • 冲突代码:

      image-20220812003235800

    • 开发工具的局限性:IDEA 2019 + Java 8

      • 代码展示:

        image-20220812003509525

      • 编译结果展示:

        image-20220812003552964

    • JDK 判定方法是否重复:

      • 方法返回值、方法名、参数;
    • 开发工具判定方法是否重复:

      • 方法名、参数;
  • JDK 内部对泛型做的优化:

    • 在 Java 虚拟机中对泛型的字节码增加了许多属性:例如,signature

      • 这个东西会记住原来的泛型类信息,
      • 在 JDK 中是一个弱记忆,通过签名字段,在字节码中保留了相关信息;