阅读 27

JAVA泛型通配符 <? extends T>与<? super T>

当泛型遇上多态

泛型

泛型,即类型的参数化。指定具体类型信息,在编译阶段进行约束。 例如:

// 指定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());
            }
        }
    }


复制代码
关注下面的标签,发现更多相似文章
评论