Java:有界通配符还是有界类型参数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3486689/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java: bounded wildcards or bounded type parameter?
提问by Tony Le
Recently, I read this article: http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html
最近看了这篇文章:http: //download.oracle.com/javase/tutorial/extra/generics/wildcards.html
My question is, instead of creating a method like this:
我的问题是,而不是创建这样的方法:
public void drawAll(List<? extends Shape> shapes){
for (Shape s: shapes) {
s.draw(this);
}
}
I can create a method like this, and it works fine:
我可以创建一个这样的方法,它工作正常:
public <T extends Shape> void drawAll(List<T> shapes){
for (Shape s: shapes) {
s.draw(this);
}
}
Which way should I use? Is wildcard useful in this case?
我应该使用哪种方式?在这种情况下通配符有用吗?
采纳答案by polygenelubricants
It depends on what you needto do. You need to use the bounded type parameter if you wanted to do something like this:
这取决于您需要做什么。如果你想做这样的事情,你需要使用有界类型参数:
public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
if (shape.isPretty()) {
shapes.add(shape);
}
}
Here we have a List<T> shapes
and a T shape
, therefore we can safely shapes.add(shape)
. If it was declared List<? extends Shape>
, you can NOTsafely add
to it (because you may have a List<Square>
and a Circle
).
这里我们有 aList<T> shapes
和 a T shape
,因此我们可以安全地shapes.add(shape)
。如果它被声明List<? extends Shape>
,你不能安全地add
使用它(因为你可能有 aList<Square>
和 a Circle
)。
So by giving a name to a bounded type parameter, we have the option to use it elsewhere in our generic method. This information is not always required, of course, so if you don't need to know that much about the type (e.g. your drawAll
), then just wildcard is sufficient.
因此,通过为有界类型参数命名,我们可以选择在泛型方法的其他地方使用它。当然,这些信息并不总是必需的,因此如果您不需要对类型(例如您的drawAll
)了解太多,那么只需通配符就足够了。
Even if you're not referring to the bounded type parameter again, a bounded type parameter is still required if you have multiple bounds. Here's a quote from Angelika Langer's Java Generics FAQs
即使您不再引用有界类型参数,如果您有多个边界,仍然需要有界类型参数。这是来自Angelika Langer 的 Java 泛型常见问题解答的引述
What is the difference between a wildcard bound and a type parameter bound?
A wildcard can have only one bound, while a type parameter can have several bounds. A wildcard can have a lower or an upper bound, while there is no such thing as a lower bound for a type parameter.
Wildcard bounds and type parameter bounds are often confused, because they are both called bounds and have in part similar syntax. […]
Syntax:
type parameter bound T extends Class & Interface1 & … & InterfaceN wildcard bound upper bound ? extends SuperType lower bound ? super SubType
A wildcard can have only one bound, either a lower or an upper bound. A list of wildcard bounds is not permitted.
A type parameter, in constrast, can have several bounds, but there is no such thing as a lower bound for a type parameter.
通配符绑定和类型参数绑定有什么区别?
通配符只能有一个界限,而类型参数可以有多个界限。通配符可以有下限或上限,而类型参数没有下限。
通配符边界和类型参数边界经常被混淆,因为它们都被称为边界并且具有部分相似的语法。[…]
语法:
type parameter bound T extends Class & Interface1 & … & InterfaceN wildcard bound upper bound ? extends SuperType lower bound ? super SubType
通配符只能有一个界限,下限或上限。不允许使用通配符边界列表。
相比之下,类型参数可以有多个界限,但没有类型参数的下界之类的东西。
Quotes from Effective Java 2nd Edition, Item 28: Use bounded wildcards to increase API flexibility:
引自Effective Java 2nd Edition,第 28 条:使用有界通配符来增加 API 灵活性:
For maximum flexibility, use wildcard types on input parameters that represent producers or consumers. […] PECS stands for producer-
extends
, consumer-super
[…]Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code. Properly used, wildcard types are nearly invisible to users of a class. They cause methods to accept the parameters they should accept and reject those they should reject. If the user of the class has to think about wildcard types, there is probably something wrong with the class's API.
为了获得最大的灵活性,在代表生产者或消费者的输入参数上使用通配符类型。[…] PECS 代表生产者-
extends
、消费者-super
[…]不要使用通配符类型作为返回类型。它不会为您的用户提供额外的灵活性,而是强制他们在客户端代码中使用通配符类型。如果使用得当,通配符类型对于类的用户几乎是不可见的。它们导致方法接受他们应该接受的参数并拒绝他们应该拒绝的参数。如果类的用户必须考虑通配符类型,则类的 API 可能有问题。
Applying the PECS principle, we can now go back to our addIfPretty
example and make it more flexible by writing the following:
应用 PECS 原则,我们现在可以回到我们的addIfPretty
示例并通过编写以下内容使其更加灵活:
public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }
Now we can addIfPretty
, say, a Circle
, to a List<Object>
. This is obviously typesafe, and yet our original declaration was not flexible enough to allow it.
现在我们可以addIfPretty
说, a Circle
,到 a List<Object>
。这显然是类型安全的,但我们最初的声明不够灵活,无法允许它。
Related questions
相关问题
- Java Generics: What is PECS?
- Can someone explain what does
<? super T>
mean and when should it be used and how this construction should cooperate with<T>
and<? extends T>
?
Summary
概括
- Do use bounded type parameters/wildcards, they increase flexibility of your API
- If the type requires several parameters, you have no choice but to use bounded type parameter
- if the type requires a lowerbound, you have no choice but to use bounded wildcard
- "Producers" have upperbounds, "consumers" have lowerbounds
- Do not use wildcard in return types
- 一定要使用有界类型参数/通配符,它们会增加 API 的灵活性
- 如果类型需要多个参数,则别无选择,只能使用有界类型参数
- 如果类型需要下限,则别无选择,只能使用有界通配符
- “生产者”有上限,“消费者”有下限
- 不要在返回类型中使用通配符
回答by Nikita Rybak
The second way is a bit more verbose, but it allows you to refer T
inside it:
第二种方式有点冗长,但它允许您在其中引用T
:
for (T shape : shapes) {
...
}
That's the only difference, as far as I understand.
据我所知,这是唯一的区别。
回答by Andrei Fierbinteanu
In your example you don't really need to use T, since you don't use that type anywhere else.
在您的示例中,您实际上并不需要使用 T,因为您不会在其他任何地方使用该类型。
But if you did something like:
但如果你做了类似的事情:
public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){
T s = shapes.get(0);
s.draw(this);
return s;
}
or like polygenlubricants said, if you want to match the type parameter in the list with another type parameter:
或者像 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);
}
}
In the first example you get a bit more type safety then returning just Shape, since you can then pass the result to a function that may take a child of Shape. For example you may pass a List<Square>
to my method, and then pass the resulting Square to a method that only takes Squares. If you used '?' you would have to cast the resulting Shape to Square which would not be type safe.
在第一个示例中,与只返回 Shape 相比,您获得了更多的类型安全性,因为您可以将结果传递给一个可能采用 Shape 子代的函数。例如,您可以将 a 传递List<Square>
给我的方法,然后将生成的 Square 传递给一个只接受 Squares 的方法。如果你使用'?' 您必须将生成的 Shape 转换为 Square,这将不是类型安全的。
In the second example you ensure that both lists have the same type parameter (which you can't do with '?', since each '?' is different), so that you can create a list that contains all elements from both of them.
在第二个示例中,您确保两个列表具有相同的类型参数(您不能使用 '?',因为每个 '?' 都不同),以便您可以创建一个包含来自它们的所有元素的列表.
回答by chammu
Consider following example from The Java Programming by James Gosling 4th edition below where we want to merge 2 SinglyLinkQueue:
考虑以下来自 James Gosling 的 The Java Programming 第 4 版的示例,我们希望合并 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
}
Both of the above methods have the same functionality. So which is preferable? Answer is 2nd one. In the author's own words :
上述两种方法具有相同的功能。那么哪个更可取?答案是第二个。用作者自己的话来说:
"The general rule is to use wildcards when you can because code with wildcards is generally more readable than code with multiple type parameters. When deciding if you need a type variable, ask yourself if that type variable is used to relate two or more parameters, or to relate a parameter type with the return type. If the answer is no, then a wildcard should suffice."
“一般规则是尽可能使用通配符,因为带有通配符的代码通常比带有多个类型参数的代码更具可读性。在决定是否需要一个类型变量时,问问自己该类型变量是否用于关联两个或多个参数,或者将参数类型与返回类型相关联。如果答案是否定的,那么通配符就足够了。”
Note: In book only second method is given and type parameter name is S instead of 'T'. First method is not there in the book.
注意:书中只给出了第二种方法,类型参数名称是 S 而不是 'T'。书中没有第一种方法。
回答by Martin Maletinsky
As far as I understand, the wildcard allows for more concise code in situations where a type parameter is not required (e.g. because it's referenced at several places or because multiple bounds are required as detailed in other answers).
据我了解,通配符允许在不需要类型参数的情况下使用更简洁的代码(例如,因为它在多个地方被引用,或者因为其他答案中详述的需要多个边界)。
In the link you indicate I read (under "Generic Methods") the following statements which hint in this direction:
在您指出的链接中,我阅读了(在“通用方法”下)以下暗示这个方向的陈述:
Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn't such a dependency, a generic method should not be used.
[...]
Using wildcards is clearer and more concise than declaring explicit type parameters, and should therefore be preferred whenever possible.
[...]
Wildcards also have the advantage that they can be used outside of method signatures, as the types of fields, local variables and arrays.
泛型方法允许使用类型参数来表达一个或多个参数的类型之间的依赖关系和/或其返回类型。如果没有这样的依赖关系,则不应使用泛型方法。
[...]
使用通配符比声明显式类型参数更清晰、更简洁,因此应尽可能首选。
[...]
通配符的另一个优点是它们可以在方法签名之外使用,如字段、局部变量和数组的类型。