java 泛型中? extends Class和? super Class的意义和PECS原则

最近在读Thinking in Java 的 Generics 章节,主要讲Java中泛型的设计理念和使用方法。

其中,关于通配符和关键字 ? 、extends、super 在泛型中的应用一直没有理清楚头绪,通过Google发现了一个有意思的问题觉得很值得记录一下,所以写下本文。

这个问题是这样的:对于class A{}class B extends A{}在如下代码中会出现编译错误:

        List<? extends A> list = new ArrayList<>();
        list.add(new B());/* exlipse error:The method add(capture#1-of ? extends A) in the type List<capture#1-of ? extends A> is not applicable for the arguments (B)*/

这个问题很奇怪,向一个List<? extends A>添加一个继承了A的对象B为什么不行?

其实虽然这句话从人的角度上理解是没毛病的,但是对Java来说,List中存放的是什么它在程序运行前完全不知道,他知道的仅仅是List中的东西可以被向上转型为A。

假如,你在运行的时候这个List中存放的是A的另一个子类class C extends A{}(不是B),对于存储C的List是不可以存储B的。

编译器是不允许你的代码在编译的时候出现这种情况的,也就是说,这个List只可以从里面取出数据(A接口)而不可以放里面放入数据。与之相对的是List<? super B>则是一个只可向里面放入数据(B的父类或本身)而不可以取出数据的List。

那么我们现在拥有了一个只读的容器和一个只写的容器,很容易想到,生产者和消费者的实现就通过这两个泛型。

我们通常称之为PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。

以下是JDK 8 Collections.copy() source code:

/**
     * 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. <p>
     *
     * This method runs in linear time.
     *
     * @param  <T> 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 <tt>set</tt> 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());
            }
        }
    }

Reference

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值