当没有使用通配符的情况下,我们定义一个方法:
1 2 3 4 |
|
我们从List中 get和set都没有问题,因为这个E 它的类型是某种明确的类型。
而当使用通配符时来描述参数时,就有些不同了。
我们先定义一下两种通配符:<? extends E>
是 Upper Bound(上限) 的通配符<? super E>
是 Lower Bound(下限) 的通配符
1) 当使用 Upper Bound 通配符时:
1 2 3 4 |
|
上面代码中通配符<?>
是 <? extends Object>
的简写。(关于<?>
是否和<? extends Object>
完全等价,我遇到个小插曲,在结束的时候来描述)
在eclipse里错误提示为:
The method set(int, capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (int, Object)
注: <capture#2-of ?>
是一个占位符,表示编译器对通配符的捕获,更多见:
http://www.ibm.com/developerworks/cn/java/j-jtp04298.html
set报错的原因是因为此时方法中的类型是不可具体化的(reified),你可以传递一个String,Number,Book,等任何继承自Object的类作为List的参数类型给test方法,而list要求集合中的类型必须是一致的,set的时候没有办法保证set进去的数据类型是否和list中原本的类型一致,比如你传给test方法的是 List<Book>, 那么在方法中set进去一个Object显然类型就不一致了。这也是通配符带来灵活性的同时所要付出的代价。
结论:使用了 <? extends E>
这样的通配符,test方法的参数list变成了只能get不能set(除了null) 或者不严谨的说它变成了只读参数了, 有些类似一个生产者,提供数据。
2) 当使用 Lower Bound 的通配符时:
1 2 3 4 5 6 7 |
|
这时get只能get出最宽泛的父类型,即Object。
这时set的时候,必须是Number或Number的子类。
原因和上面的get类似。
结论: 使用了<? super E>
这种通配符,test方法的参数list的get受到了很大的制约,只能最宽泛的方式来获取list中的数据,相当于get只提供了数据最小级别的访问权限(想想,你可能原本是放进去了一个Book,却只能当作Object来访问)。它更多适合于set的使用场景,像是一个消费者,主要用来消费数据。
上面便是对通配符的使用原则的说明,简单的说 PECS原则是指导我们在泛型方法中使用通配符的直接原则。参数作为生产者使用<? extends E>
,作为消费者时使用<? super E>
。