泛型中的 super 和 extends
很多人对 <? extends T>
和 <? super T>
这两个泛型通配符的用法不清楚,经常会出现理解无法,下面来看一下到底如何正确的使用这个组合。
<? extends T>
先来看一段代码,以下的这段代码用来遍历一个装有 Integer 数据的 list:
public void printNumberList(List<Integer> integerList) {
for (Integer i : integerList) {
System.out.println(i);
}
}
// 遍历一个 Integer List
List<Integer> integerList = Arrays.asList(1,2,3,4,5,6);
printNumberList(integerList);
如果我还想遍历另外一个装有 Double 数据的 list,我得写另外一段代码:
public void printNumberList(List<Double> doubleList) {
for (Double d : doubleList) {
System.out.println(d);
}
}
// 遍历一个 Double List
List<Double> doubleList = Arrays.asList(1.1,2.2,3.3,4.4,5.5,6.6);
printNumberList(doubleList);
在工作中这样写代码怕是不行吧?很简单就可以想到使用泛型
来对这个代码进行优化:
public <T> void printNumberGenericsList(List<T> list) {
for (T l : list) {
System.out.println(l);
}
}
// 利用泛型来遍历多种类型的 List
List<Integer> integerList = Arrays.asList(1,2,3,4,5,6);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5, 6.6);
printNumberGenericsList(integerList);
printNumberGenericsList(doubleList);
代码运行起来了,很好的解决了这个问题。其实还有问题,看下面的代码:
List<String> strList = Arrays.asList("name1", "name2", "name3");
printNumberGenericsList(strList);
本来这个方法是想用来打印数字类型的 List,而我放进了一个 String 类型的 List,也能够正常的跑起来,对于这个方法没有什么问题,但是比如说后续把这个方法内部实现改了,加上了数字的运算,再传输 String 类型的 List 就会出错了,所以在这里我想只想接受数字类型的 List,代码修改如下:
public void printNumberGenericsExtendsList(List<? extends Number> list) {
for (Number l : list) {
System.out.println(l);
}
}
List<Integer> integerList = Arrays.asList(1,2,3,4,5,6);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5, 6.6);
printNumberGenericsExtendsList(integerList);
printNumberGenericsExtendsList(doubleList);
List<String> strList = Arrays.asList("name1", "name2", "name3");
// 在编译期就无法通过
// printNumberGenericsExtendsList(strList);
现在我们知道了 <? extends T>
的用法,为了将这个问题说的更清楚,再来看下面这段代码:
List<? extends Number> l1 = new ArrayList<Number>();
List<? extends Number> l2 = new ArrayList<Integer>();
List<? extends Number> l3 = new ArrayList<Double>();
l1.add(2); // 编译不通过
l2.add(2); // 编译不通过
l3.add(3); // 编译不通过
在上面的代码中,我们声明了 3 个 list,这没什么问题,但是当我们分别向 3 个 list 中添加元素的时候,居然会报错。因为 <? extends Number>
只能保证这个 List 中的内容都是 Number
类型的,当你添加一个 Integer 时,list 实际有可能指向 List<Double>
,所以编译器不允许向 list 中添加元素,也就是说 <? extends T>
类型的 List 是只读的。
同样从 list 中获取值时,它也只能保证是 Number 类型的,所以无法从中获取 Integer 或者 Double 等类型的数据(可以取出来之后进行强制类型转换)。
也就是说 <? extends T> 中只能放 T 类型或其子类的对象。
<? super T>
搞懂了 extends
,那么 super 这个就不难懂了,看下面的代码:
List<? super Number> l1 = new ArrayList<Number>();
List<? super Number> l2 = new ArrayList<Object>();
l1.add(1); // 编译通过
l2.add(2.2); // 编译通过
这里的 list 可以添加元素,只能添加 Number 或者 Number 的子类对象。但是不能向 list 中添加 Object 的类型,因为这个 list 的实际类型有可能是 List<Number>
。
从 list 取值的时候,只能取出 Object 类型的值,因为只能保证取出来的值是 Object 的子类对象。
super T> 和 extends T> 可以组合起来使用(Collections.copy(),删减了一些代码),如下例子: ```java public static