• 泛型实现原理


    Java中的泛型是伪泛型

    泛型思想最早在C++语言的模板(Templates)中产生,Java后来也借用了这种思想。虽然思想一致,但是他们存在着本质性的不同。C++中的模板是真正意义上的泛型,在编译时就将不同模板类型参数编译成对应不同的目标代码,ClassName和ClassName是两种不同的类型,这种泛型被称为真正泛型。这种泛型实现方式,会导致类型膨胀,因为要为不同具体参数生成不同的类。

    Java中ClassName和ClassName虽然在源代码中属于不同的类,但是编译后的字节码中,他们都被替换成原始类型(ClassName),而两者的原始类型的一样的,所以在运行时环境中,ClassName和ClassName就是同一个类。Java中的泛型是一种特殊的语法糖,通过类型擦除实现(后面介绍),这种泛型称为伪泛型。由于Java中有这么一个障眼法,如果没有进行深入研究,就会在产生莫名其妙的问题。值得一提的是,不少大牛对Java的泛型的实现方式很不满意。

    类型擦除

    Java中的泛型是通过类型擦除来实现的。所谓类型擦除,是指通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。

    下面通过两个例子来证明在编译时确实发生了类型擦除。

    例1分别创建实际类型为String和Integer的ArrayList对象,通过getClass()方法获取两个实例的类,最后判断这个实例的类是相等的,证明两个实例共享同一个类。

    // 声明一个具体类型为String的ArrayList
    ArrayList<String> arrayList1 = new ArrayList<String>();  
    arrayList1.add("abc");  
    
    // 声明一个具体类型为Integer的ArrayList
    ArrayList<Integer> arrayList2 = new ArrayList<Integer>();  
    arrayList2.add(123);  
    
    System.out.println(arrayList1.getClass() == arrayList2.getClass());  // 结果为true
    

    例2创建一个只能存储Integer的ArrayList对象,在add一个整型数值后,利用反射调用add(Object o)add一个asd字符串,此时运行代码不会报错,运行结果会打印出1和asd两个值。这时再里利用反射调用add(Integer o)方法,运行会抛出codeNoSuchMethodException异常。这充分证明了在编译后,擦除了Integer这个泛型信息,只保留了原始类型。

    ArrayList<Integer> arrayList3 = new ArrayList<Integer>();
    arrayList3.add(1);
    arrayList3.getClass().getMethod("add", Object.class).invoke(arrayList3, "asd");
    for (int i = 0; i < arrayList3.size(); i++) {
        System.out.println(arrayList3.get(i)); // 输出1,asd
    }
    
    arrayList3.getClass().getMethod("add", Integer.class).invoke(arrayList3, 2); // NoSuchMethodException:java.util.ArrayList.add(java.lang.Integer)
    

    自动类型转换

    上一节上说到了类型擦除,Java编译器会擦除掉泛型信息。那么调用ArrayList的get()最终返回的必然会是一个Object对象,但是我们在源代码并没有写过Object转成Integer的代码,为什么就能“直接”将取出来的对象赋予一个Integer类型的变量呢(如下面的代码第12行)?

    import java.util.List;
    import java.util.ArrayList;
    
    /**
     * 泛型中的类型转换测试。
     */
    public class Test {
    
        public static void main(String[] args) {
            List<Integer> a = new ArrayList<Integer>();
            a.add(1);
            Integer ai = a.get(0);
        }
    }
    

    实际上,Java的泛型除了类型擦除之外,还会自动生成checkcast指令进行强制类型转换。上面的代码中的main方法编译后所对应的字节码如下。

    public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=3, args_size=1
             0: new           #2                  // class java/util/ArrayList
             3: dup
             4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
             7: astore_1
             8: aload_1
             9: iconst_1
            10: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
            13: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
            18: pop
            19: aload_1
            20: iconst_0
            21: invokeinterface #6,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
            26: checkcast     #7                  // class java/lang/Integer
            29: astore_2
            30: return
          LineNumberTable:
            line 7: 0
            line 8: 8
            line 9: 19
            line 10: 30
    }
    

    看到第18行代码就是将Object类型的对象强制转换为Integer的指令。我们完全可以将上面的代码转换为下面的代码,它所实现的效果跟上面的泛型是一模一样的。既然泛型也需要进行强制转换,所以泛型并不会提供运行时效率,不过可以大大降低编程时的出错概率。

    public static void main(String[] args) {
        List a = new ArrayList();
        a.add(1);
        Integer ai = (Integer)a.get(0);
    }
    
  • 相关阅读:
    Python os模块
    Python 常用模块
    CentOS7中配置基于Nginx+Supervisor+Gunicorn的Flask项目
    CentOS下安装Python3.4
    修改windows文件的换行符
    dubbo源码阅读-ProxyFactory(十一)之JdkProxyFactory
    dubbo源码阅读-Filter默认实现(十一)之DeprecatedFilter
    dubbo源码阅读-Filter默认实现(十一)之TimeoutFilter
    dubbo源码阅读-Filter默认实现(十一)之ExceptionFilter
    dubbo源码阅读-Filter默认实现(十一)之ContextFilter&ConsumerContextFilter
  • 原文地址:https://www.cnblogs.com/treasury/p/13229762.html
Copyright © 2020-2023  润新知