注意
想法及时记录,实现可以待做。
Java泛型
主要对泛型界限做介绍,
extends
这样的通配符声明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
,因为可以分配给foo3
的任何列表都包含Number
或Number
的子类。 - 你不可读取
Integer
,因为foo3
可能指向List<Double>
- 你不可读取
Double
,因为foo3
可能指向List<Integer>
- 你可以读取
- 写入
给定以上可能的分配,你可以将什么类型的对象添加到
List foo3
,这对于所有以上可能的ArrayList
分配都是合法的:- 你不可以添加
Integer
,因为foo3
可能会指向List<Double>
- 你不可以添加
Double
,因为foo3
可能会指向List<Integer>
- 你不可以添加
Number
,因为foo3
可能会指向List<Integer>
- 你不可以添加
你不能将任何对象添加到List<? extends T>
,因为你不能保证它真正指向的是哪种List
,因此不能保证该List
中允许该对象。
唯一能”保证”的是你只能从中读取内容,并且会获得T
或T
的子类。
super
现在考虑下 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<Number>
或List<Object>
- 你不能保证使用
Number
,因为foo3可能指向List<Object>
- 唯一的保证是你将获得一个
Object
的实例或Object
的子类(但不知道是什么子类)
- 你不能保证使用
- 写入
给定以上可能的分配,你可以将什么类型的对象添加到
List foo3
,这对于所有以上可能的ArrayList
分配都是合法的:- 你可以添加一个
Integer
,因为上述任何列表中都允许使用Integer
- 你可以添加
Integer
子类的实例,因为上述任何列表中都允许使用Integer
子类的实例 - 你不能添加
Double
,因为foo3
可能指向ArrayList<Integer>
- 你不能添加
Number
,因为foo3
可能指向ArrayList<Integer>
- 你不能添加
Object
,因为foo3
可能指向ArrayList<Integer>
- 你可以添加一个
你无法从List<? super T>
中读取特定的T
类型(例如Number
),因为你不能保证它真正指向的是哪种列表。
你能唯一保证的是你能够添加类型T
(或T
的任何子类)的值,而不会破坏所指向列表的完整性。
PECS
记住PECS:”Producer Extends, Consumer Super”。
- “Producer Extends”
如果你需要一个
List
来生产T
值(要从列表中读取T
),则需要使用? extends T
,例如List<? extends Integer>
。但是你不能添加元素到此列表中。 - “Consumer Super”
如果你需要使用
List
来消费T
值(要将T
写入列表中),则需要使用? super T
,例如List<? super Integer>
。但是并不能保证你可以从该列表中读取具体类型的对象。 - 如果你既需要读取列表也要写入列表,则需要完全不使用通配符对其进行声明,例如
List<Integer>
示例
请注意,源列表src(生产列表)如何使用extends
,而目标列表dest(消费列表)如何使用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));
}
}
// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double>());