当前位置: 首页 > 知识库问答 >
问题:

Java泛型:通配符

夏侯渊
2023-03-14

所以我在阅读泛型以重新熟悉这些概念,尤其是在涉及通配符的地方,因为我很少使用或遇到通配符。从我的阅读中,我不明白他们为什么使用通配符。下面是我经常遇到的一个例子。

void printCollection( Collection<?> c ) {
  for (Object o : c){
    System.out.println(o);
  }
}

你为什么不这样写:

<T> void printCollection( Collection<T> c ) {
    for(T o : c) {
        System.out.println(o);
    }
}

oracle网站上的另一个示例:

public static double sumOfList(List<? extends Number> list) {
    double s = 0.0;
    for (Number n : list)
        s += n.doubleValue();
    return s;
}

为什么这不是写成

public static <T extends Number> double sumOfList(List<T> list) {
    double s = 0.0;
    for (Number n : list)
        s += n.doubleValue();
    return s;
}

我错过什么了吗?

共有3个答案

百里泓
2023-03-14
匿名用户

确实,如果方法参数类型具有具有上限的第一级通配符,则可以将其替换为类型参数。

反例(通配符不能被类型参数替换)

    List<?> foo()  // wildcard in return type  

    void foo(List<List<?>> arg)   // deeper level of wildcard

    void foo(List<? super Number> arg)   // wildcard with lower bound

现在,对于可以通过通配符或类型参数解决的情况

        void foo1(List<?> arg)

    <T> void foo2(List<T> arg)

通常认为,foo1()比foo2()更时尚。这可能有点主观。我个人认为签名更容易理解。而且foo1()在行业中被广泛采用,因此最好遵循惯例。

foo1()也将处理arg更抽象一些,因为您不能轻松地处理arg。在foo1()中添加(某物)。当然,这很容易解决(即将arg传递给foo2()!)。公共方法类似于foo1()也是一种常见做法,它在内部指向私有的foo2()。

在某些情况下,通配符不起作用,需要类型参数:

    <T> void foo(List<T> foo1, List<T> foo2);  // can't use 2 wildcards.

到目前为止的讨论是关于方法签名的。在其他地方,在不能引入类型参数的地方,通配符是必不可少的。

费承载
2023-03-14

为什么要把事情弄得比需要的更复杂?这些例子展示了可能可行的最简单的事情——这些例子并不是试图说明通用方法。

你为什么不这样写:

<T> void printCollection( Collection<T> c ) {
    for(T o : c) {
        System.out.println(o);
    }
}

因为<代码>系统。出来println()可以接受对象,因此不需要更具体的内容。

为什么这不是写成

public static <T extends Number> double sumOfList(List<T> list) {
    double s = 0.0;
    for (Number n : list)
        s += n.doubleValue();
    return s;
}

同样,因为您不需要为每个不同的T扩展数字进行不同的参数化。接受列表的非泛型方法

徐高懿
2023-03-14

来自Oracle:

出现的一个问题是:何时应该使用泛型方法,何时应该使用通配符类型?为了理解答案,让我们研究一下集合库中的一些方法。

interface Collection<E> {
     public boolean containsAll(Collection<?> c);
     public boolean addAll(Collection<? extends E> c);
 }

我们可以在这里使用通用方法

interface Collection<E> {
     public <T> boolean containsAll(Collection<T> c);
     public <T extends E> boolean addAll(Collection<T> c);
     // Hey, type variables can have bounds too!
 }

然而,在包含所有和addAll中,类型参数T只使用一次。返回类型不依赖于类型参数,方法的任何其他参数也不依赖于类型参数(在这种情况下,只有一个参数)。这告诉我们类型参数被用于多态;它的唯一效果是允许在不同的调用站点使用各种实际的参数类型。如果是这种情况,应该使用通配符。通配符旨在支持灵活的子类型,这就是我们在这里试图表达的。

所以对于第一个示例,这是因为操作不依赖于类型。

第二,这是因为它只取决于数字类。

 类似资料:
  • 问题内容: 我对Java中的通用通配符有两个疑问: 和之间有什么区别? 什么是有界通配符,什么是无界通配符? 问题答案: 在你的第一个问题中,并且是有界通配符的示例。无限制的通配符看起来像,基本上就是<? extends Object>。宽松地表示泛型可以是任何类型。有界通配符(或)通过说它必须扩展特定类型(称为上限)或必须是特定类型的祖先(称为下限)来对类型进行限制。

  • 这2个功能有什么区别? 我看到了相同的输出。

  • 问题内容: 您好直接从Oracle http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html提供的Java教程中 我知道编译时的类型擦除。而且我也知道,一个类型(无界)将被Object取代。知道在编译时如何使用无界通配符进行编译吗?只是删除它,因为它是原始类型? 提前致谢。 问题答案: 假设我们有一个

  • 根据Joshua Bloch的“有效Java”一书,关于如何/何时在泛型中使用有界通配符有一个规则。这个规则就是PECS(productor-extends,Comsumer-Super)。当我研究以下示例时: 根据PECS规则,上述声明是错误的。但是我希望有一个的,并向这个传递一个。为什么不做呢? 为什么要始终使用关键字?为什么使用是错误的? 当然,这也代表了Comsumer的观点。为什么消费者

  • 我正在与Java8通配符泛型作斗争。 假设一个名为的泛型类(来自Core Java book) 是因为Java8编译器转换吗?超级经理反对,因此任何事情都是允许的?

  • 我有2个查询相关Java泛型 1)我对java泛型很陌生。我正在尝试转换通配符,如下所示: 我想从列表中找出对象的类型 2)我有以下方法: 那为什么我在试图调用复制(整数,浮点数)的时候会出现编译错误呢?这里怎么了?