(28):利用有限制通配符来提高API的灵活性

来源:互联网 发布:梦神床垫 知乎 编辑:程序博客网 时间:2024/06/05 08:20

正如我们所知参数化类型是不可变的,也就是说List<type1>和List<type2>是没有任何关系的即使type1和type2是父子类的关系。当type1参数作为泛型参数时,type2即使是type1的子类,也不能作为泛型参数。看下面的例子:

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)  
public static <T extends Comparable<? extends T>> max(          List<? extends T> list) {      Iterator<T> i = list.iterator();      T result = i.next();      while (i.hashNext()) {          T t = i.next();          if (T.compareTo(result) > 0)              result = t;      }      return result;  }  

push(e); }
这个方法编译起来是没问题的。但是有个局限性,假设有个Stack<Number>,那么pushAll只能插入Number类型的数,即使Integer是Number子类也不能插入。
幸运 的是Java提供了一种解决方法,称为有限制的通配符类型来处理这种情况。使用有限制的通配符Iterable<? extends E>即可解决这个问题(注意,确定了子类型后,第一个类型便都是自身的子类型),修改后的程序如下:

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

对应的假如我们想写一个popAll() 此时Iterable<? extends E>作为参数类型是不可以的,我们应该用Collection<? super E>如下:

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


如果参数化类型表示一个T生产者,就使用<? extends T>;如果它表示一个T消费者,就使用<? super T>。通俗点讲,就是如果是只读的就用<? super T>,如果是只写的就用
<? extends T>。如果是参数需要可写可读的,就不要用通配符。

看如下的max方法

public static <T extends Comparable<? extends T>> max(          List<? extends T> list) {      Iterator<T> i = list.iterator();      T result = i.next();      while (i.hashNext()) {          T t = i.next();          if (T.compareTo(result) > 0)              result = t;      }      return result;  }  
这段段代码会报错,意味着list.iterator没有返回Iterator<T>,因为它返回了T的一个子类型,知道了错误的原因,那就使用T的一个子类型来修改返回类型,代码如下

public static <T extends Comparable<? extends T>> max(          List<? extends T> list) {      Iterator<? extends T> i = list.iterator();      T result = i.next();      while (i.hashNext()) {          T t = i.next();          if (T.compareTo(result) > 0)              result = t;      }      return result;  }  
总之,在API中使用通配符类型虽然比较需要技巧,但是使API变得灵活的多,如果在写的是一个将被广泛使用的类库,则一定要适当地利用通配符类型,记住基本的原则:所有的comparable和comparator都是消费者。


0 0