【问题标题】:what's the type of list when the parameter's type is used in generics?当参数的类型在泛型中使用时,列表的类型是什么?
【发布时间】:2021-11-06 14:38:33
【问题描述】:
  public static void print(List<? extends Number> list) {
    for (Number n : list)
      System.out.print(n + " ");
    System.out.println();
  }

  public static void main(String[] args) {
    ArrayList<Integer> al = new ArrayList<>();
    print(al);
  }

我想知道list(参数)的类型是什么。是在打印()?是Number 吗?我认为是Number,因为如果不是Number,我们如何解释for循环中的Number n?我们通常在实际情况中使用这个吗?

【问题讨论】:

  • 这里有你需要的所有信息stackoverflow.com/a/4343547/14844306
  • list 的类型”是什么意思?你的意思是它的编译时类型还是它的运行时类型?它的编译时类型为List&lt;? extends Number&gt;,运行时类型为ArrayList(注意没有泛型)。

标签: java generics


【解决方案1】:

您的问题使用了模棱两可的措辞,但询问了我们如何谈论事物,所以这是一个问题。 list 参数的类型是 List&lt;? extends Number&gt; - 它就在文本中。显然不是Number这是一个列表。但是,您的问题听起来像是您想问的:它的组件类型是什么。

它是? extends Number。它就在文本中。不,这与Number 不同。你试图过度简化泛型,正如这个词所暗示的,你不能那样做。 概念(不是在java中,在物理/世界/数学中)并不是那么简单。

您正在查看的内容,您想要查找的词,是variance

在 java 中,泛型有 4 种不同的变体。这些都是其组件类型允许Number 的列表,但它们都是不同的:

Variance name What it looks like
Invariant List&lt;Number&gt;
Covariant List&lt;? extends Number&gt;
Contravariant List&lt;? super Number&gt;
Legacy/Raw List

我们需要 4 的原因是因为您可能想要对“数字列表”进行 4 种不同的操作。

具体来说,您可以写入数字列表并从中读取:

list.add(5.0); // 5.0 is a Double which is a kind of number

Number n = list.get(0);

有写作和阅读的例子。

如果您编写的方法同时执行这两种操作,则允许使用List&lt;Number&gt;。清楚地。想象一下你写的:

void addFiveAndAHalf(List<Number> n) {
  n.add(5.5);
}

在java中将List&lt;Integer&gt;传递给这个方法是合法的(毕竟,整数是一种数字......将5(一个Integer)传递给带有sig foo(Number n)的方法是合法的,为什么不通过List&lt;Integer&gt; 是合法的)? - 然后你会在你的整数列表中得到一个双精度值。哎呀。

因此,在基本类型是协变的(见下文)的情况下,泛型则不是。它们是不变的,只有完全正确的类型才行,子类型不够好。

但是,你确实可以读写它。

但是,如果您编写的方法只读取(从技术上讲,只执行不需要知道精确类型的事情,即不调用任何参数包含泛型的方法,例如addaddAll),那么这条规则就消失了。现在编写一个需要一个数字列表的方法,如果它所做的一切都被读取,那么它就可以被交给一个整数列表。所有整数都是数字。 但 java 不知道这一点,并且不会分析您的代码来试图弄清楚。即使它可以做到这一点,这也意味着您将来永远不能再更改该方法,因为它可能会更改该方法的签名被处理的方式。这不好:程序员需要明确地拼写出来。

你可以这样做:

void foo(List<? extends Number> n) {
   Number a = n.get(0); // legal
   n.add(5.5); // compiler error!
}

这个方法实际上是说:它不会调用列表中需要类型的任何方法(所以,没有addaddAll),因此如果你想给我一个 Number 子类型的列表,没关系。

如果您想调用需要该类型作为参数的方法,则逆变更为罕见,并且在任何返回类型中都不需要泛型类型。例如,List 的public E get(int idx) 方法,如果你从不调用它,但你确实在你的方法中调用了add,你想要逆变:任何超类型都可以,但子类型不是:

void foo(List<? super Number> n) {
   n.add(5.5); // compiles
   Number a = n.get(0); // compiler error
}

毕竟,将 Double 添加到 List&lt;Double&gt; 是可以的。也可以将其添加到 List&lt;Number&gt;List&lt;Object&gt;

如果要说的话,这个参数的类型是“A list of covariant Number”。但是 java 编码人员在互相交谈时永远不会这样说,他们最多只会说“A list of extends Number”,或者只是“A list of numbers”,而协方差在简化的非技术聊天中丢失了。

void foo(List<Number> n) {
  //

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    相关资源
    最近更新 更多