原文点此连接javascript
使用通配符的缘由:Java中的数组是协变的,可是泛型不支持协变。php
首先了解下什么是数组的协变,看下面的例子:java
Number[] nums = new Integer[10]; // OK
由于Integer是Number的子类,一个Integer对象也是一个Number对象,因此一个Integer的数组也是一个Number的数组,这就是数组的协变。数组
Java把数组设计成协变的,在必定程度上是有缺陷的。由于尽管把Integer[]赋值给Number[],Integer[]能够向上转型为Number[],可是数据元素的实际类型是Integer,只能向数组中放入Integer或者Integer的子类。若是向数组中放入Number对象或者Number其余子类的对象,对于编译器来讲也是能够经过编译的。可是运行时JVM可以知道数组元素的实际类型是Integer,当其它对象加入数组是就会抛出异常(java.lang.ArrayStoreException)。安全
泛型的设计目的之一就是保证了类型安全,让这种运行时期的错误在编译期就能发现,因此泛型是不支持协变的。例如:数据结构
List<Number> nums = new ArrayList<Integer>(); // incompatible types
当确实须要创建这种向上转型的类型关系的时候,就须要用到泛型的通配符特性了。例如:测试
List<? extends Number> nums = new ArrayList<Integer>(); // OK
class-name<?> var-name
ui
public static void print(List<?> list) {
for (Object obj : list) {
System.out.println(o);
}
}
为何要使用这样脆弱的类型?它对于许多简单的操做很是有用。例如 ,下面这个方法将用来测试一个 pair 是否包含一个 mill 引用,它不须要实际的类型。 public static boolean hasNulls (Pair<?> p) { return p.getFirstO = null | | p.getSecondO = null ; } 经过将 hasNulls 转换成泛型方法,能够避免使用通配符类型: public static <T> boolean hasNulls (Pair<T> p) 可是,带有通配符的版本可读性更强。
class-name<? extends superclass> var-name
spa
public static double sum(List<? extends Number> list) {
double s = 0.0;
for (Number num : list) {
s += num.doubleValue();
}
return s;
}
List<? extends Number> list = new ArrayList<Integer>(); // OK
List<? extends Number> list = new ArrayList<Object>(); // error
list.add(new Integer(1)); // error
list.add(null); // OK
Number n = list.get(0); // OK
Integer i = list.get(0); // error
public E get(int index) // 能够调用 public int indexOf(Object o) // 能够调用 public boolean add(E e) // 不能调用
class-name<? super subclass> var-name
设计
public static void writeTo(List<? super Integer> list) {
// ...
}
List<? super Number> list = new ArrayList<Number>(); // OK
List<? super Number> list = new ArrayList<Object>(); // OK
List<? super Number> list = new ArrayList<Integer>(); // error
list.add(new Integer(1)); // OK
list.add(new Object()); // error
Object obj = list.get(0); // OK
Integer i = list.get(0); // error
从上面上边界限定的通配符和下边界限定的通配符的特性,能够知道:
简而言之,上边界限定(extends)的通配符适合于内容的获取,而下边界限定(super)的通配符更适合于内容的存入。因此就有了一个PECS原则来很好的解释这两种通配符的使用原则。
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}