前言:向上转型是安全的,向下转型是不安全的,除非你知道List中的真实类型,否则向下转型就会报错。
List extends Number> foo3意味着下面的赋值语句都是合法的:
List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number
给定上述可能的赋值语句,能保证你从List foo3
中取出什么样类型的对象?
Number
对象,因为上面任意一个list都包含Number
对象或者Number
子类的对象(上面的Number、Integer、Double都可以转型成Number,并且是安全的,所以读取总是可以的)。如下代码就不会报错: List<? extends Number> foo4 = new ArrayList<Integer>();
Number number = foo4.get(0);
Integer
对象,因为foo3
可能指向的是List
(与其运行时发现Double转成Integer报错,不如编译时就不让从foo3
中取Integer
对象)。如下代码编译时会报Incompatible types
错的: List<? extends Number> foo4 = new ArrayList<Integer>();
Integer number = foo4.get(0);
因为编译的时候编译器只知道foo4引用是一个List extends Number>,要到运行时才会绑定到new ArrayList
Double
对象,因为foo3
可能指向的是List
。给定上述可能的赋值语句,你能往List foo3
中添加什么类型的对象从而保证它对于所有可能的ArrayList
都是合法的呢?
Integer
对象,因为foo3
可能指向的是List
。如下代码是会编译报错的: List<? extends Number> foo4 = new ArrayList<Integer>();
foo4.add(new Integer(1));
因为编译期间是无法知道foo4指向的ArrayList中到底放的是什么类型,只有到运行时才知道(就是Java所谓的晚绑定或运行时绑定)。与其到运行时发现往一个ArrayList
Double
对象,因为foo3
可能指向的是List
。Number
对象,因为foo3
可能指向的是List
。总结一下:你不能往List extends T>
中添加任何对象,因为你不能保证List
真正指向哪个类型,所以不能确定添加的对象就是List
所能接受的类型。能保证的,仅仅是你可以从List
中读取的时候,你获得的肯定是一个T
类型的对象(即使是T
类型的子类对象也是T
类型的)。
现在考虑List super T>
包含通配符的声明List super Integer> foo3
意味着下面任何一个赋值语句都是合法的:
List<? super Integer> foo3 = new ArrayList<Integer>(); // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>(); // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>(); // Object is a superclass of Integer
给定上述可能的赋值语句,当读取List foo3
中的元素的时候,你能保证接收到什么类型的对象呢?
Integer
对象,因为foo3
可能指向一个List
或者List
。Number
对象,因为foo3
可能指向一个List
。Object
类的实例或者Object
子类的实例(但是你不知道到底是哪个子类)。给定上述可能的赋值语句,你能往List foo3
中添加什么类型的对象从而保证它对于所有可能的ArrayList
都是合法的呢?
Integer
实例,因为Integer
类型对于上述所有的list都是合法的。Integer
子类的实例,因为一个Integer
子类的实例都可以向上转型成上面列表中的元素类型。Double
类型,因为foo3
可能指向的是ArrayList
。Number
类型,因为foo3
可能指向的是ArrayList
。Object
类型,因为foo3
可能指向的是ArrayList
。PECS是"Producer Extends,Consumer Super"(生产者用Extends,消费者用Super)的缩写。
List
去生产T
类型values(也就是说你需要去list中读取T
类型实例),你需要声明这个List
中的元素为? extends T
,例如List extends Integer>
,但是你不能往里面添加元素。List
去消费T
类型values(也就是说你需要往list中添加T
类型实例),你需要声明这个List
中的元素为? super T
,例如List super Integer>
。但是不能保证你从这个list中读取出来对象类型。?
,必须用精确的类型,比如List
。原文链接:
https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java
原文内容如下:
The wildcard declaration of List extends Number> foo3 means that any of these are legal assignments:
List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number
Now consider List super T>.
The wildcard declaration of List super Integer> foo3 means that any of these are legal assignments:
List<? super Integer> foo3 = new ArrayList<Integer>(); // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>(); // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>(); // Object is a superclass of Integer
PECS
Remember PECS: “Producer Extends, Consumer Super”.
“Producer Extends” - If you need a List to produce T values (you want to read Ts from the list), you need to declare it with ? extends T, e.g. List extends Integer>. But you cannot add to this list.
“Consumer Super” - If you need a List to consume T values (you want to write Ts into the list), you need to declare it with ? super T, e.g. List super Integer>. But there are no guarantees what type of object you may read from this list.
If you need to both read from and write to a list, you need to declare it exactly with no wildcards, e.g. List.
Example
Note this example from the Java Generics FAQ. Note how the source list src (the producing list) uses extends, and the destination list dest (the consuming list) uses super:
public class Collections {
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));
}
}
另外,可以参考JDK中的Collections类的copy方法,有助于记住PECS规则:
/**
* Copies all of the elements from one list into another. After the
* operation, the index of each copied element in the destination list
* will be identical to its index in the source list. The destination
* list must be at least as long as the source list. If it is longer, the
* remaining elements in the destination list are unaffected.
*
* This method runs in linear time.
*
* @param the class of the objects in the lists
* @param dest The destination list.
* @param src The source list.
* @throws IndexOutOfBoundsException if the destination list is too small
* to contain the entire source List.
* @throws UnsupportedOperationException if the destination list's
* list-iterator does not support the set operation.
*/
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
参考文章:https://www.cnblogs.com/suxuan/p/4970467.html