集合类使用通配符的问题
在实验3中,设计可复用的API时,由于我的接口PlanningEntryAPIs
要同时复用于航班管理、高铁车次管理、学习日程管理三个APP,而这3个APP的传给我API中的方法的类型都不同。例如,对于判断资源冲突的方法,我需要传一个含有计划项的List
,而不同的APPList
存储的计划项类型不同(航班是PlaneEntry
、高铁是TrainEntry
、学习日程是ActEntry
),但是这三个计划项类型都是继承父类PlanningEntry
的,因此我考虑使用含有通配符的List<? extends PlanningEntry>
来进行传递参数,但是在具体使用中却遇到了问题。
1、什么是通配符
对于集合类的泛型T,在具体使用时,实际上会产生类型擦除的状况,也就是说无论向集合类传入什么参数,在运行时都会把泛型T给擦除成Object。具体实现可参照下面的代码
擦除之前:
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
//...
}
擦除之后
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
//...
}
因此,因为类型擦除的存在,像List<String>
、List<Integer>
等就不是List<Object>
的子类型。我们知道对一个方法进行传参,是可以将子类型传给父类型的,但是如果我们的参数定义成List<Object>
,却不能将List<String>
、List<Integer>
等传给它,这造成了很大的困扰。
于是就有了泛型统配符的存在,我们参数写了List<?>
,这个参数就可以接受List<String>
、List<Integer>
了。
更进一步,除了无界通配符?
,还有上界和下界通配符
1、上界通配符: < ? extends E>
,它表示参数化的类型可能是所指定的类型,或者是此类型的子类。
2、下届通配符: < ? super E>
,它表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object
。
顺带一提,尽管在传参时List<String>
和List<Object>
是两个完全没关系的类型,但是判断overload时候是相等的,所以父类是List<Object>
,子类是List<String>
,或者父类是List<String>
,子类是List<Object>
,那么仍然不是overload(因为参数列表相同)。
2、集合类使用泛型通配符时调用add方法
对于集合类,调用add方法时有比较严格的限制,用简单的两句话来概括:
1、无界通配符?
,只能add空对象null
2、上界通配符< ? extends E>
,同样只能add空对象null
3、下界通配符< ? super E>
,能够addE
本身和E
的子类型
举几个简单的例子
1、无界通配符?
List<?> list = new ArrayList<Number>();
Number a = 1;
list.add(null);//编译没有问题
list.add(a);//编译错误不兼容的类型: java.lang.Number无法转换为capture#1, 共 ?
2、上界通配符< ? extends E>
List<? extends Number> list1 = new ArrayList<>();
Integer a = 1;
list1.add(null);//编译没有问题
list1.add(a);//编译错误,不兼容的类型: java.lang.Integer无法转换为capture#1, 共 ? extends java.lang.Number
List<? extends Integer> list2 = new ArrayList<>();
Number b = 1;
list2.add(null);//编译没有问题
list1.add(b);//编译错误,不兼容的类型: java.lang.Number无法转换为capture#1, 共 ? extends java.lang.Integer
3、下界通配符< ? super E>
List<? super Number> list1 = new ArrayList<>();
Integer a = 1;
Double b = 2.5;
list1.add(null);//编译没有问题
list1.add(a);//编译没有问题
list2.add(b);//编译没有问题
List<? super Integer> list1 = new ArrayList<>();
Number c = 1;
list2.add(c);//编译错误,不兼容的类型: java.lang.Number无法转换为capture#1, 共 ? super java.lang.Integer
那么为什么只有< ? super E>
能addE
本身和E
的子类型,而< ? extends E>
和?
却只能add空对象null
呢?
这是因为对于集合内的类型E
,是可以调用add方法添加E
及E
的子类型的。
< ? super E>
代表的是E
和E
的父类型,而E
本身和E
的子类型必然是< ? super E>
的子类型,所以可以正常调用add方法添加。
但是< ? extends E>
和?
代表的类型可能是E
的子类型,对于这样一个未知的E
的子类型(不妨记作R
),没法确保我们add的类型是R
的子类型,所以不能调用add方法添加。