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

有限制的通配符类型

        修改前:

public void pushAll(Iterable<E> src) {
	for (E e : src) {
		push(e)
	}
}
        修改后:

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

pushAll的输入参数类型不应该为“E的Iterable接口”,而应该为“E的某个子类型的Iterable接口”,有一个通配符类型正符全此意:Iterable<? Extends E>。(确定了子类型后,每个类型都是自身的子类型,即便它没有将自身扩展。)


为了获得最大限度的灵活性,要在表示生产者和消费者的输入参数上使用通配符类型。如果某个输入参数即是生产者,又是消费都,那么通配符类型对你就没有什么好处了,因为你需要严格的类型匹配,这是不用任何通配符而得到的。

下面的助记符便于让你记住要使用哪种通配符类型:

PECS 表示:producer-extends, consumer-super

换句话说,如果参数化类型表示一个T生产者,就使用<? extends T>;如果它表示一个T消费者,就使用<? super T>。在我们的Stack示例中,pushAll的src参数产生E实例供Stack使用,因此src相应的类型为Iterable<? extends E>;popAll的dst参数通过Stack消费E实例,因此dst相应的类型为Collection<? super E>。PECS这个助记符突出了使用通配符的基本原则。Naftalin和Wadler称之为Get and Put Principle

类型参数和通配符之间具有双重性,许多方法都可以利用其中一个或者另一个进行声明。例如,下面是可能的两种静态方法声明,来交换列表中的两个被索引的项目。第一个使用无限制的类型参数,第二个使用无限制的通配符

public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i , int j);
在公共API中,第二种更好一些,因为它更简单。将它传到一个列表中--------任何列表----------方法就会交换被索引的元素。不用担心类型参数。一般来说,如果类型参数只在方法声明中出现一次,就可以用通配符取代它。如果是无限制的类型参数,就用无限制的通配符取代它;如果是有限制的类型参数,就用有限制的通配符取代它


总而言之,在API中使用通配符类型虽然比较需要技巧,但是使API变得灵活得多。如果编写的是将被广泛使用的类库,则一定要适当地利用通配符。记住基本的原则:producer-extends, consumer-super(PECS)还要记住所有的comparable和comparator都是消费者。

摘抄自:Effective Java


发布了25 篇原创文章 · 获赞 5 · 访问量 4万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章