• 第28条:利用有限制通配符来提升API的灵活性


    参数化类型是不可变的。对两个不同类型T1和T2而言,List<T1>与List<T2>没有父子类型关系。

    考虑:

    public class Stack<E> {
        public Stack();
        public void push(E e);
        public E pop();
        public boolean isEmpty();
    }

    假设增加一个方法,按顺序将一系列的元素放到堆栈中:

    public void pushAll(Iterable<E> src) {
        for(E e : src)
            push(e);
    }

    如果尝试这样做:

    Stack<Number> s = new Stack<Number>();
    Iterable<Integer> i = ...;
    s.pushAll(integers);

    从逻辑上讲,这样应该是允许的,因为Integer是Number的子类,应当允许将Integer放到类型为Number的堆栈中。

    但实际运行的时候会提示Iterable<Number>与Iterable<Integer>不兼容。

    有限制的通配符类型可以处理这种情况:

    pushAll的输入参数不应该是“E的Iterable接口”,而应该为“E的某个子类型的Iterable接口”,修改为Iterable<? extends E>

    假设添加一个popAll方法,从堆栈中弹出每个元素,添加到指定集合中:

    public void popAll(Collection<E> dst) {
        while(!isEmpty) {
            dst.add(pop());
        }
    }

    与未修改的putAll一样,应当允许类型为Number的栈帧放在包括Number在内的父类型中。

    所以,修改为:

    public void popAll(Collection<? super E> dst) {
        while(!isEmpty) {
            dst.add(pop());
        }
    }

    在putAll方法中,输入参数的角色是生产者,因为参数提供数据给堆栈使用,在popAll方法中,输入参数的角色是消费者,因为堆栈提供数据给参数使用。总之,如果参数化类型表示一个T生产者,就使用<? extends T>,如果表示一个T的消费者,就使用<? super T>。

  • 相关阅读:
    【转】 MySQL高级知识(一)——基础
    inline元素的间距问题
    ES6对于数组的扩展
    JavaScript的垃圾回收机制
    call() apply() bind()
    防抖和节流
    Promise
    js的事件机制
    Javascript异步操作的异常处理
    JavaScript的事件执行机制及异步
  • 原文地址:https://www.cnblogs.com/13jhzeng/p/5726511.html
Copyright © 2020-2023  润新知