最近,我读到了这篇文章:http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html
我的问题是,与其创建这样的方法,不如:
public void drawAll(List<? extends Shape> shapes){
for (Shape s: shapes) {
s.draw(this);
}
}
我可以创建一个这样的方法,它工作得很好:
public <T extends Shape> void drawAll(List<T> shapes){
for (Shape s: shapes) {
s.draw(this);
}
}
我应该用哪种方式?通配符在这种情况下有用吗?
发布于 2010-08-15 16:29:22
这取决于你需要做什么。如果要执行以下操作,则需要使用绑定类型参数:
public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
if (shape.isPretty()) {
shapes.add(shape);
}
}
这里我们有一个List<T> shapes
和一个T shape
,因此我们可以安全地shapes.add(shape)
。如果它被声明为List<? extends Shape>
,您就不能安全地add
到它(因为您可能有一个List<Square>
和一个Circle
)。
因此,通过为一个有界类型参数命名,我们可以选择在我们的泛型方法中的其他地方使用它。当然,这些信息并不总是必需的,所以如果你不需要知道太多关于类型的信息(例如你的drawAll
),那么通配符就足够了。
即使您不再引用有界类型参数,如果您有多个边界,则仍然需要有一个有界类型参数。下面是Angelika Langer's Java Generics FAQs的一句话
通配符绑定和类型参数绑定有什么区别?
通配符只能有一个界限,而类型参数可以有多个界限。通配符可以有下限,也可以有上限,而类型参数没有下限。通配符界限和类型参数界限经常被混淆,因为它们都被称为界限,并且在某种程度上具有相似的语法。…
语法:
类型参数绑定T扩展了类& Interface1 &…& InterfaceN通配符界限上限?扩展了SuperType的下界?超级SubType
通配符只能有一个界限,可以是下界,也可以是上界。不允许使用通配符界限列表。
在constrast中,一个类型参数可以有几个界限,但没有类型参数的下限。
引用自Effective Java第二版,第28项:使用有界通配符增加API灵活性
为了获得最大的灵活性,请在表示生产者或消费者的输入参数上使用通配符类型。…PECS代表producer-
extends
,consumer-super
…
不使用通配符类型作为返回类型。它将强制用户在客户端代码中使用通配符类型,而不是为用户提供额外的灵活性。如果使用得当,类的用户几乎看不到通配符类型。它们导致方法接受它们应该接受的参数,并拒绝它们应该拒绝的参数。如果类的用户必须考虑通配符类型,那么类的接口可能有问题。
应用PECS原则,我们现在可以返回到我们的addIfPretty
示例,并通过编写以下代码使其更加灵活:
public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }
现在我们可以addIfPretty
,比方说,Circle
,to a List<Object>
。这显然是类型安全的,但我们的原始声明还不够灵活,不能允许它。
相关问题
摘要
如果类型需要多个参数,您别无选择,只能使用绑定类型参数
types
发布于 2010-08-15 16:50:54
在您的示例中,您实际上不需要使用T,因为您在其他任何地方都不使用该类型。
但是如果你做了类似这样的事情:
public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){
T s = shapes.get(0);
s.draw(this);
return s;
}
或者像多基因润滑剂所说的,如果你想将列表中的类型参数与另一个类型参数相匹配:
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);
}
}
在第一个示例中,您获得了比仅返回形状更多的类型安全性,因为您可以将结果传递给可能接受Shape的子级的函数。例如,您可以将一个List<Square>
传递给我的方法,然后将结果Square传递给一个只接受Square的方法。如果你使用'?‘您将不得不将结果形状强制转换为Square,这将不是类型安全的。
在第二个示例中,您将确保两个列表具有相同的类型参数(不能使用'?',因为每个列表都有'?‘是不同的),这样您就可以创建一个包含这两个元素的所有元素的列表。
发布于 2015-07-19 08:26:20
考虑下面James Gosling第四版的Java Programming中的示例,其中我们想要合并2个SinglyLinkQueue:
public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
// merge s element into d
}
public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
// merge s element into d
}
上述两种方法都具有相同的功能。那么哪一个更可取呢?答案是第二个。用作者自己的话说:
“一般规则是尽可能使用通配符,因为使用通配符的代码通常比具有多个类型参数的代码更具可读性。在决定是否需要类型变量时,请问问自己,该类型变量是用于关联两个或多个参数,还是用于关联参数类型和返回类型。如果答案是否定的,则通配符应该就足够了。”
注意:在书中只给出了第二种方法,类型参数名称是S而不是'T‘。书中没有介绍第一种方法。
https://stackoverflow.com/questions/3486689
复制相似问题