Bootstrap

Java类型擦除

什么是类型擦除

Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。

问1:Java类型擦除是什么过程出现的?

Java泛型编译过程会擦除掉泛型信息。

    /**
     * 比较两个ArrayList的类型比较
     * 1) 第一个ArrayList是ArrayList<String>,第二个是ArrayList<Integer>
     *  实际运行时获取的类型都是java.util.ArrayList
     *
     * @author zhouronghua
     * @time 2022/1/10 4:15 下午
     */
    @Test
    public void testGenericTypeCompare() {
        System.out.println("测试泛型类比较");
        // 第一个是字符串ArrayList
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("One");
        list1.add("Two");
        // 第二个是整形ArrayList
        ArrayList<Integer> list2 = new ArrayList<>();
        list2.add(100);
        list2.add(200);
        // 比较两个ArrayList类型
        System.out.println("ArrayList Type compare: " + (list1.getClass() == list2.getClass()));
        // java.util.ArrayList
        System.out.println("ArrayList<String> Type: " + list1.getClass());
        // java.util.ArrayList
        System.out.println("ArrayList<Integer> Type: " + list2.getClass());
    }

编译后的类型进行了擦除,都是ArrayList,限定类型都已经擦除了。

我们通过查看字节码,就可以清晰的看到ArrayList的类型限定擦除了。添加元素的时候,

对象类型使用的是Object

2.类型擦除后保留的原始类型 

原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

问2:是否能够通过instanceof查询ArrayList的类限定类型(泛型信息)?

不能。

2.Java协变和逆变

因为泛型类型编译过程中会发生类型擦除,那么怎么将子类的发行模板的对象,传递给父类的泛型模板使用。

例如:Integer集成Number,根据里氏替代原则:

Number number = new Integer(100);

对象创建的时候,可以直接将Integer对象赋值给number。

如果泛型中也希望达到这种效果,将子类的泛型赋值给父类泛型使用,怎么处理呢?可以通过通配符进行类型限定,从而实现协变

// 采用通配符实现协变
ArrayList<? extends Number> list1 = new ArrayList<Integer>();

协变:将父类保持了子类型的继承关系。通过协变实现子类型的泛型类型可以赋值给父类型泛型。

逆变:逆转了子类型的关系。将父类型泛型赋值给子类型泛型。

不变:两种关系都不满足

3.Java逆变

怎么将ArrayList<Number>赋值给ArrayList<Integer>呢?

通过? super实现逆变

ArrayList<? super Integer> list2 = new ArrayList<Number>();

参考文案:
Java泛型中的类型擦除详解 - 知乎

;