当泛型遇上多态
泛型
泛型,即类型的参数化。指定具体类型信息,在编译阶段进行约束。 例如:
// 指定ArrayList参数类型为String
List<String> stringList = new ArrayList<String>();
// 指定ArrayList参数类型为Integer
List<Integer> integerList = new ArrayList<Integer>();
多态
多态机制,在JAVA语言中是同封装、继承并列的三大特性之一;是构建灵活、可扩展性程序的基础。
其通常表现形式是父类引用指向子类对象。
例如:
// 使用Number类型(父类类型)接收,并不关心new的是Integer还是Float(子类类型)
Number number = new Integer(0);
多态的使用,在变量赋值、方法调用、模块交互等都有着广泛的使用。
当泛型遇上多态
参数化类型不能直接使用多态,即按照多态的思路直接使用参数化类型会编译错误。
ArrayList<Object> list = new ArrayList<String>();// error
正确的使用姿势是泛型通配符 <? extends T> <? super T>。
scala中类似的语义是泛型的协变、逆变,可对照理解。
<? extends T>
即泛型上界,类型约束为T类型的子类。如:
// T为Object,String是Object的子类,满足约束
ArrayList<? extends Object> list = new ArrayList<String>();//ok
list.add("abc");// error
可以理解为ArrayList<? extends Object>是ArrayList<String>的父类类型,可以接收ArrayList<String>。
在scala中类似的语义是泛型的协变。
- 为什么不能存元素呢?
规定的是上界为Object类型,那么Object子类都可以存,当然实际类型String的父类型也是可以的,那么就会出现子类引用指向父类对象的情况。 所以编译器禁止添加元素。
- 为什么能取元素呢?
规定的是上界,可以以上界的类型(父类类型)接收对象,不会出现问题。
<? super T>
即泛型下界,类型约束为T的基类。如:
//T为String,Object为String的基类,满足约束
ArrayList<? super String> list = new ArrayList<Object>();//ok
list.add("abc");//ok
Object s = list.get(0);// Object
System.out.println(s);
可以理解为ArrayList<? super String>是ArrayList<Object>的父类类型,可以接收ArrayList<String>。
在scala中类似的语义是泛型的逆变。
- 为什么可以存元素呢?
规定的是下界为String类型,即所有String及其子类都可以存(可能不太恰当,String是final的)。String及String的子类都是实际类型Object的子类。 所以,添加元素不会有问题。
- 为什么取出来的元素是Object类型
定义的为泛型下界,不能确定是哪一级父类,使用Object一定不会错。
CEPS原则
CEPS(Consumer extends product super)指<? extends T>用于get数据;<? super T>用于set数据。
JDK集合工具类Collections的copy实现,完美体现CEPS原则
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());
}
}
}