您的位置:首页 > 其它

Item 28 利用有限制通配符来提升API的灵活性

2015-04-19 17:29 295 查看
<? 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);

[align=left]public class Union {[/align]
[align=left] [/align]
[align=left] public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2) {[/align]
[align=left] Set<E> result = new HashSet<E>(s1);[/align]
[align=left] result.addAll(s2);[/align]
[align=left] return result;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] // Simple program to exercise flexible generic method[/align]
[align=left] public static void main(String[] args) {[/align]
[align=left] Set<Integer> integers = new HashSet<Integer>();[/align]
[align=left] integers.add(1);[/align]
[align=left] integers.add(3);[/align]
[align=left] integers.add(5);[/align]
[align=left] [/align]
[align=left] Set<Double> doubles = new HashSet<Double>();[/align]
[align=left] doubles.add(2.0);[/align]
[align=left] doubles.add(4.0);[/align]
[align=left] doubles.add(6.0);[/align]
[align=left] [/align]
[align=left] // Won't compile; see page 137[/align]
[align=left] // Set<Number> numbers = union(integers, doubles);[/align]
[align=left] [/align]
[align=left] // Explicit type parameter is necessary here[/align]
[align=left] Set<Number> numbers = Union.<Number> union(integers, doubles);[/align]
[align=left] [/align]
[align=left] System. out.println(numbers);[/align]
[align=left] }[/align]
[align=left]}[/align]

------------------------------------------------------------------------------
使用有限制的通配符,让比较操作不只是限定在一种具体类型上:

版本一:

[align=left]class Parent implements Comparable<Parent> {[/align]
[align=left] [/align]
[align=left] @Override[/align]
[align=left] public int compareTo(Parent o) {[/align]
[align=left] // TODO Auto-generated method stub[/align]
[align=left] //实现比较留空[/align]
[align=left] return 0;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]}[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]class ChildOne extends Parent {[/align]
[align=left] [/align]
[align=left]}[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]class ChildTwo extends Parent {[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]}[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]public class RecursiveTypeBound {[/align]
[align=left] // Returns the maximum value in a list - uses recursive type bound[/align]
[align=left] public static <T extends Comparable<T>> T max(List<T> list) {[/align]
[align=left] Iterator<T> i = list.iterator();[/align]
[align=left] T result = i.next();[/align]
[align=left] while (i.hasNext()) {[/align]
[align=left] T t = i.next();[/align]
[align=left] if (t.compareTo(result) > 0)[/align]
[align=left] result = t;[/align]
[align=left] }[/align]
[align=left] return result;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] public static void main(String[] args) {[/align]
[align=left] List<String> argList = Arrays. asList(args);[/align]
[align=left] System.out.println( max(argList));[/align]
[align=left] [/align]
[align=left] List<ChildOne> oneList = new ArrayList<ChildOne>();[/align]
[align=left]// max(oneList);[/align]
[align=left]// Bound mismatch: The generic method max(List<T>) of type RecursiveTypeBound is[/align]
[align=left]// not applicable for[/align]
[align=left]// the arguments (List<ChildOne>). The inferred type ChildOne is not[/align]
[align=left]// a valid substitute for the bounded parameter <T extends Comparable<T>>[/align]
[align=left] }[/align]
[align=left]}[/align]

分析:<T extends Comparable<T>> ,该类型参数有如下限定:针对可以与自身进行比较的每个类型T。
但是,使用ChildOne类型的容器oneList,作为参数调用max方法,执行是失败的。
失败的原因是,ChildOne实例可以与其它继承了Parent的类的实例进行比较,比如,ChildTwo实例。这样就违反了约束条件:只能与自身进行比较(Comparable<T>)。

版本二:使用有限制的通配符来解决版本一的问题

[align=left]class Parent implements Comparable<Parent> {[/align]
[align=left] [/align]
[align=left] @Override[/align]
[align=left] public int compareTo(Parent o) {[/align]
[align=left] // TODO Auto-generated method stub[/align]
[align=left] return 0;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]}[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]class ChildOne extends Parent {[/align]
[align=left] [/align]
[align=left]}[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]class ChildTwo extends Parent {[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]}[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]public class RecursiveTypeBound {[/align]
[align=left] public static <T extends Comparable<? super T>> T max(List<? extends T> list) {[/align]
[align=left] Iterator<? extends T> i = list.iterator();[/align]
[align=left] T result = i.next();[/align]
[align=left] while (i.hasNext()) {[/align]
[align=left] T t = i.next();[/align]
[align=left] if (t.compareTo(result) > 0)[/align]
[align=left] result = t;[/align]
[align=left] }[/align]
[align=left] return result;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] public static void main(String[] args) {[/align]
[align=left] List<String> argList = Arrays. asList(args);[/align]
[align=left] System.out.println( max(argList));[/align]
[align=left] [/align]
[align=left] List<ChildOne> oneList = new ArrayList<ChildOne>();[/align]
[align=left] max(oneList);[/align]
[align=left] [/align]
[align=left] }[/align]
[align=left]}[/align]
[align=left] [/align]

分析:新定义的泛型方法,首先,它使用的类型参数是:可以与自身进行比较的类型;可以与与该类型本身是类型兼容的类型相比较的类型。这是将Comparable<T>转换为Comparable<? super T>。
比如,ChildOne可以与ChildOne进行比较;ChildOne可以与ChildTwo进行比较。

从版本一转换为版本二,List<T>转换为List<? extends T>,转换的规则是,按照生产者消费者规则,List是生产者,使用<? extends T>。含义变化为,max不再接受单一一种类型,它可以接受某一类的类型。
比如:
[align=left] List<Parent> oneList = new ArrayList<Parent>();[/align]
[align=left] oneList.add(new ChildOne());[/align]
[align=left] oneList.add(new ChildTwo());[/align]
[align=left] oneList.add(new Parent());[/align]
[align=left] max(oneList);[/align]
由接受一种具体的类型,变为接受一个种类的类型。

Comparable<T>是消费者,它是使用参数List的数据来进行操作的。转换它的时候,使用Comparable<? super T>。

----------------------------------------------------------------------------------------------------------------------------------

类型参数和通配符
List<E>和List<?>

很多方法可以同时使用这两种方式来实现。
比如,交换列表中的元素:

版本1:

[align=left]public class SwapE {[/align]
[align=left] [/align]
[align=left] public static <E> void swap(List<E> list, int i, int j) {[/align]
[align=left] list.set(i, list.set(j, list.get(i)));[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] public static void main(String[] args) {[/align]
[align=left] // Swap the first and last argument and print the resulting list[/align]
[align=left] List<String> argList = Arrays. asList(args);[/align]
[align=left] swap(argList, 0, argList.size() - 1);[/align]
[align=left] System. out.println(argList);[/align]
[align=left] }[/align]
[align=left]}[/align]

[align=left] [/align]
[align=left]版本2:[/align]
[align=left] [/align]

[align=left]public class Swap {[/align]
[align=left] public static void swap(List<?> list, int i, int j) {[/align]
[align=left] swapHelper(list, i, j);[/align]
[align=left] //list.set(i, list.set(j, list.get(i)));,因为只能往List<?>放null。[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] // Private helper method for wildcard capture[/align]
[align=left] private static <E> void swapHelper(List<E> list, int i, int j) {[/align]
[align=left] list.set(i, list.set(j, list.get(i)));[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] public static void main(String[] args) {[/align]
[align=left] // Swap the first and last argument and print the resulting list[/align]
[align=left] List<String> argList = Arrays. asList(args);[/align]
[align=left] swap(argList, 0, argList.size() - 1);[/align]
[align=left] System. out .println(argList);[/align]
[align=left] }[/align]
[align=left]}[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: