Ton*_* Le 68 java generics api bounded-wildcard
最近,我读了这篇文章:http: //download.oracle.com/javase/tutorial/extra/generics/wildcards.html
我的问题是,而不是创建这样的方法:
public void drawAll(List<? extends Shape> shapes){
for (Shape s: shapes) {
s.draw(this);
}
}
Run Code Online (Sandbox Code Playgroud)
我可以创建一个这样的方法,它工作正常:
public <T extends Shape> void drawAll(List<T> shapes){
for (Shape s: shapes) {
s.draw(this);
}
}
Run Code Online (Sandbox Code Playgroud)
我应该使用哪种方式?在这种情况下,通配符是否有用?
pol*_*nts 114
这取决于你需要做什么.如果你想做这样的事情,你需要使用有界类型参数:
public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
if (shape.isPretty()) {
shapes.add(shape);
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,我们有一个List<T> shapes
和一个T shape
,因此我们可以安全shapes.add(shape)
.如果它被宣布List<? extends Shape>
,你不能安全地add
(因为你可能有a List<Square>
和a Circle
).
因此,通过为有界类型参数指定名称,我们可以选择在泛型方法的其他位置使用它.当然,并不总是需要这些信息,因此如果您不需要了解类型(例如您的drawAll
),那么只需使用通配符即可.
即使您没有再次引用有界类型参数,如果您有多个边界,仍然需要有界类型参数.以下是Angelika Langer的Java Generics FAQs的引用
通配符绑定和类型参数绑定有什么区别?
通配符只能有一个绑定,而类型参数可以有多个边界.通配符可以具有下限或上限,而不存在类型参数的下限.
通配符边界和类型参数边界经常被混淆,因为它们都被称为边界并且具有部分类似的语法.[...]
语法:
Run Code Online (Sandbox Code Playgroud)type parameter bound T extends Class & Interface1 & … & InterfaceN wildcard bound upper bound ? extends SuperType lower bound ? super SubType
通配符只能有一个绑定,可以是下限或上限.不允许使用通配符边界列表.
一个类型参数,在约束中,可以有几个边界,但没有类型参数的下限.
从行情有效的Java第二版,第28项:使用界通配符增加API的灵活性:
为获得最大的灵活性,请在表示生产者或使用者的输入参数上使用通配符类型 [...] PECS代表生产者
extends
,消费者 -super
[...]不要将通配符类型用作返回类型.它不会为您的用户提供额外的灵活性,而是迫使他们在客户端代码中使用通配符类型.正确使用的通配符类型对于类的用户几乎是不可见的.它们使方法接受它们应该接受的参数并拒绝它们应该拒绝的参数.如果类的用户必须考虑通配符类型,那么类的API可能有问题.
应用PECS原则,我们现在可以回到我们的addIfPretty
示例,并通过编写以下内容使其更加灵活:
public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }
Run Code Online (Sandbox Code Playgroud)
现在,我们可以addIfPretty
说,a Circle
,a List<Object>
.这显然是类型安全的,但我们的原始声明不够灵活,不允许它.
在您的示例中,您实际上不需要使用T,因为您在其他任何地方都不使用该类型.
但如果你做了类似的事情:
public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){
T s = shapes.get(0);
s.draw(this);
return s;
}
Run Code Online (Sandbox Code Playgroud)
或者像polygenlubricants所说,如果你想匹配列表中的类型参数与另一个类型参数:
public <T extends Shape> void mergeThenDraw(List<T> shapes1, List<T> shapes2) {
List<T> mergedList = new ArrayList<T>();
mergedList.addAll(shapes1);
mergedList.addAll(shapes2);
for (Shape s: mergedList) {
s.draw(this);
}
}
Run Code Online (Sandbox Code Playgroud)
在第一个示例中,您可以获得更多的类型安全性,然后只返回Shape,因为您可以将结果传递给可能需要Shape子项的函数.例如,您可以传递List<Square>
给我的方法,然后将生成的Square传递给仅采用Squares的方法.如果你用'?' 你必须将生成的Shape转换为Square,这不是类型安全的.
在第二个示例中,您确保两个列表具有相同的类型参数(您不能使用'?',因为每个'?'都不同),因此您可以创建一个包含来自两个列表的所有元素的列表.