ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-02-16 20:04:10  阅读:191  来源: 互联网

标签:wildcards Use Java int List list wildcard type public


因为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)
  • List<? extends T> list很容易理解,因为list产出T实例,所以用extends
  • 这里T不但extends Comparable<T>更能扩展成是Comparable的super T实例类,如何理解

我们假设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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有