什么是类型擦除
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泛型中的类型擦除详解 - 知乎