这取决于你需要做什么。如果你想做这样的事情,你需要使用有界类型参数:
public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
if (shape.isPretty()) {
shapes.add(shape);
}
}
这里我们有 aList<T> shapes
和 a T shape
,因此我们可以安全地shapes.add(shape)
. 如果它被声明List<? extends Shape>
,你不能安全地add
使用它(因为你可能有 aList<Square>
和 a Circle
)。
因此,通过为有界类型参数命名,我们可以选择在泛型方法的其他地方使用它。当然,并不总是需要这些信息,因此如果您不需要了解太多关于类型的信息(例如您的drawAll
),那么通配符就足够了。
即使您不再引用有界类型参数,如果您有多个边界,仍然需要有界类型参数。这是来自Angelika Langer 的 Java 泛型常见问题解答的引述
通配符绑定和类型参数绑定有什么区别?
通配符只能有一个界限,而类型参数可以有多个界限。通配符可以有下限或上限,而类型参数没有下限。
通配符界限和类型参数界限经常被混淆,因为它们都被称为界限,并且在某种程度上具有相似的语法。[…]
语法:
type parameter bound T extends Class & Interface1 & … & InterfaceN
wildcard bound
upper bound ? extends SuperType
lower bound ? super SubType
通配符只能有一个界限,即下限或上限。不允许使用通配符边界列表。
相反,一个类型参数可以有多个界限,但没有类型参数的下限之类的东西。
引自Effective Java 2nd Edition,Item 28:使用有界通配符来提高 API 灵活性:
为了获得最大的灵活性,请在代表生产者或消费者的输入参数上使用通配符类型。[...] PECS 代表生产者extends
-,消费者super
- [...]
不要使用通配符类型作为返回类型。它不会为您的用户提供额外的灵活性,而是会迫使他们在客户端代码中使用通配符类型。如果使用得当,通配符类型对类的用户几乎是不可见的。它们使方法接受它们应该接受的参数并拒绝它们应该拒绝的参数。如果该类的用户必须考虑通配符类型,则该类的 API 可能有问题。
应用 PECS 原则,我们现在可以回到我们的addIfPretty
示例,并通过编写以下代码使其更加灵活:
public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }
现在我们可以addIfPretty
说,a Circle
,到 a List<Object>
。这显然是类型安全的,但我们最初的声明不够灵活,无法允许。
相关问题
概括
- 请务必使用有界类型参数/通配符,它们会增加 API 的灵活性
- 如果类型需要多个参数,你别无选择,只能使用有界类型参数
- 如果类型需要下限,则别无选择,只能使用有界通配符
- “生产者”有上限,“消费者”有下限
- 不要在返回类型中使用通配符