在严格的泛型代码中,带泛型声明的类总是应该带着类型参数。Java也允许在使用带泛型声明的类时不指定实际的类型。但如果没有为这个泛型类指定实际类型,此时被称为raw type(原始类型),默认是声明该泛型形参时指定的第一个上限类型。
把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有尖括号之间的类型信息都被扔掉。比如一个List<String>类型被转换为List,该List对集合元素的类型检查变成了泛型参数的上限(即Object),下面程序示范了这种擦除。
1 class Apple<T extends Number> 2 { 3 T size; 4 public Apple() 5 {} 6 public Apple(T size) 7 { 8 this.size=size; 9 } 10 public void setSize(T size) 11 { 12 this.size=size; 13 } 14 public T getSize() 15 { 16 return this.size; 17 } 18 } 19 20 public class ErasureTest 21 { 22 public static void main(String[] args) 23 { 24 Apple<Integer> a=new Apple<>(6); 25 //a的getSize()方法返回Integer对象 26 Integer as=a.getSize(); 27 //把a对象赋给Apple变量,失去尖括号里的类型信息 28 Apple b=a; 29 //b只知道size的类型为Number 30 Number size1=b.getSize(); 31 //下面代码将引起错误 32 Integer size2=b.getSize();//错误: 不兼容的类型: Number无法转换为Integer 33 } 34 }
上面代码声明了一个带泛型声明的类Apple类,其泛型形参的上限为Number,这个泛型形参用来定义Apple的size变量。所以调用a.getSize()方法时返回Integer类型值。当把a变量赋值给一个不带泛型信息的b变量时,编译器就会丢失a对象的泛型信息,即尖括号里的信息都会丢失——因为Apple泛型形参的上限是Number类,所以编译器依然知道b的getSzie()方法返回Number类型,但具体是Number的哪个子剋就不清楚。
从逻辑上看,List<String>类是List类的子类,如果直接把一个List对象赋给一个List<String>对象应该引起编译错误,但实际上不会。对于泛型而言,可以直接把一个List对象赋值给一个List<String>对象,编译器仅仅提示“未经检查的转换”,例如:
1 import java.util.ArrayList; 2 import java.util.List; 3 public class ErasureTest2 4 { 5 public static void main(String[] args) 6 { 7 List<Integer> li=new ArrayList<>(); 8 li.add(6); 9 li.add(9); 10 System.out.println(li);//[6, 9] 11 List list=li;//擦除 12 //下面代码将引起警告“未经检查的转换”,编译、运行时完全正确 13 List<String> ls=list; 14 15 //但只要访问ls的元素,将引起运行时异常 16 System.out.println(ls.get(0)); 17 //Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String 18 //(java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap') 19 } 20 }
上面程序中定义了一个List<Integer>对象,这个List<Integer>对象保留了元素的类型信息。当执行List list=li;时,编译器会丢失前者的泛型信息,即丢失list集合里的元素的类型信息,这就是典型的擦除。Java又允许直接把List对象赋给一个List<type>类型的变量,所以执行List<String> ls=list;编译可以通过,只是会发出警告:未经检查的转换。但对list变量实际上引用的是List<Integer>集合,所以试图把集合中的元素当成String类型的对象取出时,将会引发ClassCastException异常。