8.5 擦除和转换

在严格的泛型代码中,带泛型声明的类总是应该带着类型参数。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异常。 

猜你喜欢

转载自www.cnblogs.com/weststar/p/12613412.html
8.5