首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Java:有界通配符还是有界类型参数?

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

Stack Overflow用户
提问于 2010-08-15 16:19:09
回答 5查看 40.2K关注 0票数 89

最近,我读到了这篇文章:http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html

我的问题是,与其创建这样的方法,不如:

代码语言:javascript
复制
public void drawAll(List<? extends Shape> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

我可以创建一个这样的方法,它工作得很好:

代码语言:javascript
复制
public <T extends Shape> void drawAll(List<T> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

我应该用哪种方式?通配符在这种情况下有用吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-08-15 16:29:22

这取决于你需要做什么。如果要执行以下操作,则需要使用绑定类型参数:

代码语言:javascript
复制
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示例,并通过编写以下代码使其更加灵活:

代码语言:javascript
复制
public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }

现在我们可以addIfPretty,比方说,Circle,to a List<Object>。这显然是类型安全的,但我们的原始声明还不够灵活,不能允许它。

相关问题

摘要

如果类型需要多个参数,您别无选择,只能使用绑定类型参数

  • 如果类型需要下界,则别无选择,只能使用有界类型有上界,“lowerbounds

  • Do”
  • in

types

票数 142
EN

Stack Overflow用户

发布于 2010-08-15 16:50:54

在您的示例中,您实际上不需要使用T,因为您在其他任何地方都不使用该类型。

但是如果你做了类似这样的事情:

代码语言:javascript
复制
public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){
    T s = shapes.get(0);
    s.draw(this);
    return s;
}

或者像多基因润滑剂所说的,如果你想将列表中的类型参数与另一个类型参数相匹配:

代码语言:javascript
复制
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,这将不是类型安全的。

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

票数 6
EN

Stack Overflow用户

发布于 2015-07-19 08:26:20

考虑下面James Gosling第四版的Java Programming中的示例,其中我们想要合并2个SinglyLinkQueue:

代码语言:javascript
复制
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‘。书中没有介绍第一种方法。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3486689

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档