Java:有界通配符还是有界类型参数?

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的引用

通配符绑定和类型参数绑定有什么区别?

通配符只能有一个绑定,而类型参数可以有多个边界.通配符可以具有下限或上限,而不存在类型参数的下限.

通配符边界和类型参数边界经常被混淆,因为它们都被称为边界并且具有部分类似的语法.[...]

语法:

  type parameter bound     T extends Class & Interface1 & … & InterfaceN

  wildcard bound  
      upper bound          ? extends SuperType
      lower bound          ? super   SubType
Run Code Online (Sandbox Code Playgroud)

通配符只能有一个绑定,可以是下限或上限.不允许使用通配符边界列表.

一个类型参数,在约束中,可以有几个边界,但没有类型参数的下限.

从行情有效的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>.这显然是类型安全的,但我们的原始声明不够灵活,不允许它.

相关问题


摘要

  • 使用有界类型参数/通配符,它​​们可以提高API的灵活性
  • 如果类型需要多个参数,则别无选择,只能使用有界类型参数
  • 如果类型需要下限,则别无选择,只能使用有界通配符
  • "生产者"有上限,"消费者"有下限
  • 不要在返回类型中使用通配符

  • 另外 - 你不能像 `doWork(? type)` 这样将 `?` 作为方法参数传递,因为这样你就不能在方法中使用该参数。您需要使用类型参数。 (2认同)

And*_*anu 5

在您的示例中,您实际上不需要使用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,这不是类型安全的.

在第二个示例中,您确保两个列表具有相同的类型参数(您不能使用'?',因为每个'?'都不同),因此您可以创建一个包含来自两个列表的所有元素的列表.