编程语言
首页 > 编程语言> > [Effective Java] Item 28: Use bounded wildcards to increase API flexibility

[Effective Java] Item 28: Use bounded wildcards to increase API flexibility

作者:互联网

因为parameterized type是invariant的,换句话说,也就是两个不同的类,type1和type2,无论type1和type2是什么关系,List<type1>既不是List<type2>的subtype也不是supertype。

这样设计generic类别时会有局限性,还是拿Stack来举例:

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

	// pushAll method without wildcard type - deficient!
    public void pushAll(Iterable<E> src) {
      for (E e : src)
        push(e);
    }
   // popAll method without wildcard type - deficient!
   public void popAll(Collection<E> dst) {
     while (!isEmpty())
        dst.add(pop());
    }
}

此时我们多加入两个方法,pushAll如果只接受Iterable<E>,如果此时是个Stack<Number>类,它只能接受Iterable<Number>,即使Integer是Number的子类,它也不能接受Iterable<Integer>。同理可得,popAll也有相同的缺陷。

//改进
// Wildcard type for a parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
    for (E e : src)
        push(e);
}

// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {
    while (!isEmpty())
        dst.add(pop());
}

// 如此使用便不会报compiler error
Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);

Collection<Object> objects = ... ;
numberStack.popAll(objects);

总结:
for maximum flexibility, use wildcard types on input parameters that represent producers or consumers。为了方便记,我们用PECS缩写来记,代表producer extends, consumer super.

上面Stack这个例子中,pushAll’s src produces E instances for use by the Stack; popAll’s dst consumes E instances from the Stack.

所以这里,wildcard types扩展了方法能接受和拒绝的类型,但不要在方法的return type上使用wildcard types。我们重新看下Item27的union方法,做以下修改:

public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2)

使用:
Set<Integer>  integers =  ...
Set<Double>   doubles  =  ...
Set<Number>   numbers  =  union(integers, doubles);

这里compiler在java 8之前会报错,complier无法做出正确的type判断,所以我们需要明确的标识出return type
Set<Number>   numbers  =  Union.<Number>union(integers, doubles);

同时我们来看下Item27的max方法

//修改前
public static <T extends Comparable<T>> T max(List<T> list)
// 加入 bounded wildcard type
public static <T extends Comparable<? super T>> T max(List<? extends T> list)

我们假设Student是Person的一个子类,我们让Student implements Comparable<Person>

public class Person {}

public class Student extends Person implements Comparable<Person> {
    @Override public int compareTo(Person that) {
        // ...
    }
}

这样的compareTo方法是按照Person实例的属性实现的。(当然实际应用时,你很难看到这种情况,因为Person会实现那个Comparable接口,Student会继承那个方法的) 所以你可以把List<Student>放入Collection.sort方法中,这样的排序会按照上面实现的compareTo的Person属性进行排序。

同样适用于 Comparator<? super T>

class ByAgeAscending implements Comparator<Person> {
    @Override public int compare(Person a, Person b) {
        return a.getAge() < b.getAge();
    }
}

List<Student> students = getSomeStudents();
Collections.sort(students, new ByAgeAscending());

结论:无论对于Comparable还是Comparator of T,都是consumes T 实例,然后产生Intergers代表顺序。所以always使用 Comparable<? super T> 和 Comparator<? super T> in preference to Comparable<T> 和 Comparator<T>

//完整版max方法实现
public static <T extends Comparable<? super T>> T max(List<? extends T> list){
	Iterator<? extends T> i = list.iterator(); // returns an iterator of some subtype of T
	T result = i.next(); // returns some subtype of T, and can be safely stored in a variable of T
	while(i.hasNext()){
		T t = i.next();
		if(t.compareTo(result) > 0)
			result = t;
	}
}

最后一个话题,许多方法既能用type parameter也可以用wildcard来定义。比如我们写一个静态方法,交换列表中的两个元素。两种写法:

public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j); // Better!!!

结论: 如果方法的声明中,type parameter只出现过一次,那就用wildcard来取代它。unbounded type parameter - unbounded wildcard type, bounded type parameter - bounded wildcard type

但是,我们也知道上面的方法因为是unbounded wildcard type of list,它不能放除了null以外的value,所以我们会使用一个辅助方法。

public static void swap(List<?> list, int i, int j){
	swapHelper(list, i, j);
}
// 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)))
}

这里swapHelper方法知道list是一个List<E>, 知道从list中取出来的object是type E的,而且把一个type of E的object放到list里面也是安全的。这么设计的好处是,对外展现一个非常clean的API,一个wildcard-based方法声明的API,并且也很好的利用了里面内部方法调用了个比较复杂的一个generic方法。

标签:wildcards,Use,Java,int,List,list,wildcard,type,public
来源: https://blog.csdn.net/jzhong720/article/details/113741172