二、泛型通配符讲解
通配符的使用以及代码演示
1.无限通配符>的使用:可以传入任何引用数据类型
A 在调用方法时使用?通配符的过程中无法使用add方法。原因分析:因为通配符?代表任意的数据类型,但是当我们调用的时候或者用在方法的声明上,其实这个时候还是没有给?通配符一个指定的引用数据类型,那么Java出于安全起见,就不可能允许添加元素。
B 以上的add方法虽然无法调用,add(null)是例外。原因分析:因为null可以给任意引用数据类型赋值,代表任意引用数据,但是很容易引起NullPointerException。
C 注意使用List>和List当作形参时的作用不能等同,比如当传入List时List>可以接收,但是List无法接收。原因分析:因为?代表任何参数类型可以接收,但是List中虽然Object是所有子类的父类,但是List不是List的父类,List是ArrayList等类的父类,这就是为什么泛型前后要一致的原因,从数组的角度去理解集合就比如Object[ ] arr不是Integer[ ] arr1的父类。
1 packagecom.test;2
3 importjava.util.ArrayList;4 importjava.util.List;5
6 public classInterviewList {7
8 public static voidmain(String[] args) {9 //ArrayList list2 = new ArrayList();//泛型前后要一致
10 ArrayList> list = new ArrayList();11 //list.add("122");//注意此处无法添加"122"字符串,但是可以添加null,因为null可以给任何引用数据类型赋值
12 list.add(null);//添加null没有报错
13 Object o = list.get(0);14 System.out.println(o);15
16 ArrayList list1 = new ArrayList();17 list1.add(34);18 //Error:(18, 24) java: 不兼容的类型: java.util.ArrayList无法转换为java.util.List19 //报错了,原因因为这里要接收的是List,那么可以接收的有ArrayList或者List等与List有继承关系的集合,但是无法接收ArrayList,因为和List没有继承关系,写成List>才能使用20 //printCollection1(list1);
21
22 printCollection(list1);//? 通配符可以正常接收
23 }24
25 public static void printCollection(List> col) {//此方法使用了无限通配符
26 for(Object object : col) {27 System.out.println(object);28 }29 }30
31 public static void printCollection1(List col) {//此方法没有使用泛型通配符
32 for(Object object : col) {33 System.out.println(object);34 }35 }36 }
View Code
2.上界通配符 extends E>的使用:可以传入E和E的子类
A extends E>作为形参时例如List extends E>可以使用集合的get方法来获取E或者E类型本身的元素。原因分析:当我们用get方法时我们其实是在获取集合里内部的元素,但是我们的集合的数据类型还没有确定,但是我们可以获得一些明确的已知条件,那就是在 extends E>中最大的类型是E,而且这个E最大是Object,所以我们可以利用这一点,那么我们就可以清楚地了解到该集合里面的获取的元素肯定是E或者Object的子类,他们的范围肯定小于E或者Object,那么我们就可以用Object和E这两个范围比集合里面的元素大的类去接收集合里面的元素。(注:可能略显啰嗦但是我就是想解释清楚。)
B 在使用上界通配符时,无法调用add方法来添加非null的元素。原因分析:由于上面已经说得很清楚了, extends E>作为形参时例如List extends E>这时最大类型是E和Object,但是我们不清楚最小的类型是什么,因为此时?这个通配符没有被赋值,我们调用add方法是要添加集合元素或者集合元素的子类,但是我们没法明确肯定该集合元素类型,或者比该集合元素范围更小的子类,那么Java就不会允许添加元素。
1 public classWildCardTest2 {2
3 public static voidmain(String[] args) {4 ArrayList extends Number> list = new ArrayList<>();5 //list.add(3);报错了,无法添加非null元素
6 list.add(null);//没有报错
7 Object o = list.get(0);//用Object接收没有报错
8 Number n = list.get(0);//用Number接收没有报错
9 }10
11 }
3.下界通配符 super E>的使用:可以传入E或者E的父类
A 在使用下界通配符时,无法使用get方法获取Object以外的元素,或者需要向下转型,但是可能出现ClassCastException的异常。原因分析:上界通配符,在使用get方法的时候,此时类型没有明确还是问号?我们只能明确其最大父类或者接口时,我们才能接收,但是我们只能明白 super E>作为形参时例如List super E>时,只能明确Object是最大父类,其他的一概不知,所以只能Object o = list.get(0)。
B 可以使用集合的add方法添加E或者E的子类。
原因分析:上界通配符已经解释很清楚了,add方法添加元素时,?类型不确定就要明确该?类型的最小子类,只要比可能存在的最小子类或者子接口小的任意引用数据类型的对象,我们都可以将其添加,而下界通配符 super E>当作形参时例如List super E>,此时E就是最小子类,此时add方法可以添加E或者E的子类。
1 public classWildCardTest3 {2
3 public static voidmain(String[] args) {4 ArrayList super Number> list = new ArrayList<>();5 list.add(3);//没有报错,自动装箱成Integer,Number的子类
6 list.add(3.4F);//没有报错,自动装箱成Float,Number的子类
7 list.add(32L);//没有报错,自动装箱成Long,Number的子类
8 Object o = list.get(1);9 //Integer i = list.get(0);//报错了,无法用Integer接收
10 }11
12 }
对PECS原则的解读
1.什么是PECS原则?
PECS是Producer Extends Consumer Super的递归缩写,是Java中使用泛型通配符的原则。
2.阿里巴巴的通配符使用规约
泛型通配符 extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法,而 < ? super T> 不能使用 get 方法,做为接口调用赋值时易出错。
说明:扩展说一下 PECS 原则:第一、频繁往外读取内容的,适合用 extends E>。第二、经常往里插入的,适合用 super E> 。
3.对PECS原则的简单解读
字面意思是生产者要被继承要被当作上界通配符 extends E>的上界E,消费者要继承其他类要被当成下界通配符 super E>的下界E,再借助下阿里巴巴的泛型开发规约去理解下,应该就是当这个被传入的类型需要进行很多get操作获取数据的话,那么请使用上界通配符这时这个上界就如同生产者一样,因为它能被不断get到,而当需要不断进行add方法添加数据的话,请使用下界通配符这时这个下界就如同消费者一样,因为它不断地索取,因为我们在不断地add元素给它。