zoukankan      html  css  js  c++  java
  • Item 28 利用有限制通配符来提升API的灵活性

    <? extends E> 和<? super E>的使用
     
    <? extends E>的使用
    Item 26
    这是堆栈的公有API:
    public class Stack<E>{
     
       public Stack();
       public void push(E e);
       public E pop();
       public boolean isEmpty();
     
       public void pushAll(Iterable<E> src);
    }
     
    在使用方法pushAll时,添加src,其中的数据类型要跟当前的Stack完全相同,这样才可以使用。比如,Stack<Number>,那么src的类型就只能是Iterable<Number>,不能是Iterable<Integer>。因为,Iterable<Number>与Iterable<Integer>并不是同一个类型。
     
    那么,如果要实现一种效果,可以添加的类型是Stack中元素类型的子类型,而不一定使用完全相同的类型。
    可以通过有限制的通配符来实现:
    public class Stack<E>{
     
       public Stack();
       public void push(E e);
       public E pop();
       public boolean isEmpty();
     
       public void pushAll(Iterable<? extends E> src);
    }
     
     
    ------------------------------------------------------------------------
    <? super E>的使用
     
    public class Stack<E>{
     
       public Stack();
       public void push(E e);
       public E pop();
       public boolean isEmpty();
     
       public void pushAll(Iterable<? extends E> src);
       public void popAll(Collection<E> dst);
    }
    要实现一个功能,将堆栈中的元素弹出来,保存到一个容器中。也就是实现popAll这个API。使用这个API是有要求的,那就是容器的类型要完全跟当前的Stack的类型一致。那么,假设,我们想堆栈中的元素可以存放在容器Collection<Object>中,那么,该如何重新定义popAll方法?
     
    使用super关键字。
    public class Stack<E>{
     
       public Stack();
       public void push(E e);
       public E pop();
       public boolean isEmpty();
     
       public void pushAll(Iterable<? extends E> src);
       public void popAll(Collection<? super E> dst);
    }
    修改后的popAll方法,可以保存堆栈弹出的元素的容器类型是这样的,容器类型是Stack元素类型的父类。
     
     
    好处:使用了上述的通配符,会提高API的灵活性,让它可以接受更多的类型。
    pushAll 是数据的生产者;对生产者的进参数使用 <? extends E>,可以接受更多的类型,而不是只是E这种类型,可以接受E及其子类的类型。
    popAll是数据的消费者;对消费者出参数使用<? super E>,可以让堆栈的数据保存在多种类型的容器中,而不只是保存在Collection<E>。它可以保存在类型是E的父类的容器中。
     
     
    -----------------------------------------------------------------------------------------------------------------------------------------------
    类型推导失效的情况:
     
    调用方法Set<Number> numbers = Union.union(integers, doubles);Java编译器在进行类型推导时,不能确定类型参数究竟是使用哪个(Integer,Double),在这种情况下要显示指定类型参数。Set<Number> numbers = Union.<Number> union(integers, doubles);
     
     
    public class Union {
     
            public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2) {
                  Set<E> result = new HashSet<E>(s1);
                  result.addAll(s2);
                   return result;
           }
     
            // Simple program to exercise flexible generic method
            public static void main(String[] args) {
                  Set<Integer> integers = new HashSet<Integer>();
                  integers.add(1);
                  integers.add(3);
                  integers.add(5);
     
                  Set<Double> doubles = new HashSet<Double>();
                  doubles.add(2.0);
                  doubles.add(4.0);
                  doubles.add(6.0);
     
                   // Won't compile; see page 137
                   // Set<Number> numbers = union(integers, doubles);
     
                   // Explicit type parameter is necessary here
                  Set<Number> numbers = Union.<Number> union(integers, doubles);
     
                  System. out.println(numbers);
           }
    }
     
    ------------------------------------------------------------------------------
    使用有限制的通配符,让比较操作不只是限定在一种具体类型上:
     
     
    版本一:
    class Parent implements Comparable<Parent> {
     
      @Override
      public int compareTo(Parent o) {
        // TODO Auto-generated method stub
        //实现比较留空
        return 0;
      }
     
     
    }
     
     
    class ChildOne extends Parent {
     
    }
     
     
    class ChildTwo extends Parent {
     
     
    }
     
     
    public class RecursiveTypeBound {
      // Returns the maximum value in a list - uses recursive type bound
      public static <T extends Comparable<T>> T max(List<T> list) {
        Iterator<T> i = list.iterator();
        T result = i.next();
        while (i.hasNext()) {
          T t = i.next();
          if (t.compareTo(result) > 0)
            result = t;
        }
        return result;
      }
     
      public static void main(String[] args) {
        List<String> argList = Arrays. asList(args);
        System.out.println( max(argList));
       
        List<ChildOne> oneList = new ArrayList<ChildOne>();
    //    max(oneList);
    //    Bound mismatch: The generic method max(List<T>) of type RecursiveTypeBound is
    //    not applicable for
    //    the arguments (List<ChildOne>). The inferred type ChildOne is not
    //    a valid substitute for the bounded parameter <T extends Comparable<T>>
      }
    }
     
    分析:<T extends Comparable<T>> ,该类型参数有如下限定:针对可以与自身进行比较的每个类型T。
    但是,使用ChildOne类型的容器oneList,作为参数调用max方法,执行是失败的。
    失败的原因是,ChildOne实例可以与其它继承了Parent的类的实例进行比较,比如,ChildTwo实例。这样就违反了约束条件:只能与自身进行比较(Comparable<T>)。
     
     
    版本二:使用有限制的通配符来解决版本一的问题
    class Parent implements Comparable<Parent> {
     
      @Override
      public int compareTo(Parent o) {
        // TODO Auto-generated method stub
        return 0;
      }
     
     
    }
     
     
    class ChildOne extends Parent {
     
    }
     
     
    class ChildTwo extends Parent {
     
     
    }
     
     
    public class RecursiveTypeBound {
      public static <T extends Comparable<? super T>> T max(List<? extends T> list) {
        Iterator<? extends T> i = list.iterator();
        T result = i.next();
        while (i.hasNext()) {
          T t = i.next();
          if (t.compareTo(result) > 0)
            result = t;
        }
        return result;
      }
     
      public static void main(String[] args) {
        List<String> argList = Arrays. asList(args);
        System.out.println( max(argList));
     
        List<ChildOne> oneList = new ArrayList<ChildOne>();
        max(oneList);
     
      }
    }
     
    分析:新定义的泛型方法,首先,它使用的类型参数是:可以与自身进行比较的类型;可以与与该类型本身是类型兼容的类型相比较的类型。这是将Comparable<T>转换为Comparable<? super T>。
    比如,ChildOne可以与ChildOne进行比较;ChildOne可以与ChildTwo进行比较。
     
     
    从版本一转换为版本二,List<T>转换为List<? extends T>,转换的规则是,按照生产者消费者规则,List是生产者,使用<? extends T>。含义变化为,max不再接受单一一种类型,它可以接受某一类的类型
    比如:
     List<Parent> oneList = new ArrayList<Parent>();
        oneList.add(new ChildOne());
        oneList.add(new ChildTwo());
        oneList.add(new Parent());
        max(oneList);
    由接受一种具体的类型,变为接受一个种类的类型。
     
    Comparable<T>是消费者,它是使用参数List的数据来进行操作的。转换它的时候,使用Comparable<? super T>。
     
     
    ----------------------------------------------------------------------------------------------------------------------------------
     
     
    类型参数和通配符
    List<E>和List<?>
     
    很多方法可以同时使用这两种方式来实现。
    比如,交换列表中的元素:
     
    版本1:
     
    public class SwapE {
     
      public static <E> void swap(List<E> list, int i, int j) {
        list.set(i, list.set(j, list.get(i)));
      }
     
      public static void main(String[] args) {
        // Swap the first and last argument and print the resulting list
        List<String> argList = Arrays. asList(args);
        swap(argList, 0, argList.size() - 1);
        System. out.println(argList);
      }
    }
     
    版本2:
     
    public class Swap {
            public static void swap(List<?> list, int i, int j) {
                   swapHelper(list, i, j);
                   //list.set(i, list.set(j, list.get(i)));,因为只能往List<?>放null。
           }
     
            // Private helper method for wildcard capture
            private static <E> void swapHelper(List<E> list, int i, int j) {
                  list.set(i, list.set(j, list.get(i)));
           }
     
            public static void main(String[] args) {
                   // Swap the first and last argument and print the resulting list
                  List<String> argList = Arrays. asList(args);
                   swap(argList, 0, argList.size() - 1);
                  System. out .println(argList);
           }
    }
     
     
  • 相关阅读:
    Android游戏开发22:Android动画的实现J2me游戏类库用于Android开发
    android sqlite SQLiteDatabase 操作大全 不看后悔!必收藏!看后精通SQLITE (第三部分,完整代码)
    使用OGR创建dxf格式矢量数据
    mysql 数据库引擎 MyISAM InnoDB 大比拼 区别
    android sqlite SQLiteDatabase 操作大全 不看后悔!必收藏!看后精通SQLITE (第二部分)
    mysql 更改数据库引擎
    android sqlite SQLiteDatabase 操作大全 不看后悔!必收藏!看后精通SQLITE (第一部分)
    android 数字键盘使用
    MySQL Innodb数据库性能实践
    eclipse : Error while performing database login with the driver null
  • 原文地址:https://www.cnblogs.com/ttylinux/p/4439503.html
Copyright © 2011-2022 走看看