java 泛型实现原理

 

泛型思想最早在C++语言的模板(Templates)中产生,Java后来也借用了这种思想。虽然思想一致,但是他们存在着本质性的不同。

C++中的模板是真正意义上的泛型,在编译时就将不同模板类型参数编译成对应不同的目标代码,List<Integer>和List<String>是两种不同的类型,这种泛型被称为真正泛型。

这种泛型实现方式,会导致类型膨胀,因为要为不同具体参数生成不同的类。

 

Java中List<Integer>和List<String>虽然在源代码中属于不同的类,但是编译后的字节码中,他们都被替换成原始类型,而两者的原始类型的一样的(List<Object>),所以在运行时,List<Integer>与List<String>就是同一个类。

Java中的泛型是一种特殊的语法,通过类型擦除实现,这种泛型称为伪泛型。

类型擦除,是指将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。

类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。

 

        List<Integer> intList = new ArrayList<>();
        intList.add(3);
        List<String> strList  = new ArrayList<>();
        strList.add("Hello");

        System.out.println(intList.getClass() == strList.getClass());  // 输出结果为true

intList与strList都属于同一个类。

 

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

 

创建一个List<Integer>对象intList,利用反射调用其add()方法,向intList中添加一个String类元素,运行代码不会报错。

        List<Integer> intList = new ArrayList<>();
        intList.add(3);

        intList.getClass().getMethod("add", Object.class).invoke(intList, "Hello");
        for (int i = 0; i < intList.size(); i++) {
            System.out.println(intList.get(i)); 
        }

        // 输出结果为
        // 3
        // Hello

这说明在编译后,擦出了Integer这个泛型信息,intList为原始类型List<Object>。

 

修改代码getMethod("add", Object.class),改为getMethod("add", Integer.class)

//      NoSuchMethodException: 
        intList.getClass().getMethod("add", Integer.class).invoke(intList, "Hello");

运行报错,intList的类List<Integer>中没有("add", Integer.class)方法,只有("add", Object.class)。

 

 

自动类型转换

Java的泛型除了类型擦除之外,还会自动生成checkcast指令进行强制类型转换。

        List<Integer> intList = new ArrayList<>();
        intList.add(3);

        int a = intList.get(0);

使用intList的get方法返回的是Integer类型的对象。

 

posted on 2018-07-30 15:51  Deltadeblog  阅读(445)  评论(0编辑  收藏  举报

导航