为什么菱形运算符在 Java 7 中的 addAll() 调用中不起作用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7555652/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 20:28:45  来源:igfitidea点击:

Why doesn't the diamond operator work within a addAll() call in Java 7?

javagenericsjava-7diamond-operator

提问by Pradeep Kumar

Given this example from the generics tutorial.

鉴于泛型教程中的这个例子。

List<String> list = new ArrayList<>();
list.add("A");

// The following statement should fail since addAll expects
// Collection<? extends String>

list.addAll(new ArrayList<>());

Why does the last line not compile, when it seems it should compile. The first line uses a very similar construct and compiles without a problem.

为什么最后一行没有编译,而它似乎应该编译。第一行使用了一个非常相似的构造并且编译没有问题。

Please explain elaborately.

请详细解释。

回答by Joachim Sauer

First of all: unless you're using Java 7 all of this will not work, because the diamond <>has only been introduced in that Java version.

首先:除非您使用 Java 7,否则所有这些都不起作用,因为菱形<>仅在该 Java 版本中引入。

Also, this answer assumes that the reader understands the basics of generics. If you don't, then read the other partsof the tutorial and come back when you understand those.

此外,此答案假设读者了解泛型的基础知识。如果您不知道,请阅读教程的其他部分,并在您理解这些内容后回来。

The diamond is actually a shortcut for not having to repeat the generic type information when the compiler could find out the type on its own.

菱形实际上是一种快捷方式,当编译器可以自己找出类型时,不必重复泛型类型信息。

The most common use case is when a variable is defined in the same line it's initialized:

最常见的用例是在初始化变量的同一行中定义变量时:

List<String> list = new ArrayList<>(); // is a shortcut for
List<String> list = new ArrayList<String>();

In this example the difference isn't major, but once you get to Map<String, ThreadLocal<Collection<Map<String,String>>>>it'll be a majorenhancement (note: I don'tencourage actually using such constructs!).

在这个例子中,差别并不大,但是一旦你掌握了Map<String, ThreadLocal<Collection<Map<String,String>>>>它,它将是一个重大的改进(注意:我鼓励实际使用这样的结构!)。

The problem is that the rules only go that far. In the example above it's pretty obvious what type should be used and both the compiler and the developer agree.

问题是规则只能做到这一点。在上面的例子中,很明显应该使用什么类型并且编译器和开发人员都同意。

On this line:

在这一行:

list.addAll(new ArrayList<>());

it seemsto be obvious. At least the developer knows that the type should be String.

似乎很明显。至少开发人员知道类型应该是String.

However, looking at the definition of Collection.addAll()we see the parameter type to be Collection<? extends E>.

但是,查看 的定义,Collection.addAll()我们看到参数类型为Collection<? extends E>

It means that addAllaccepts any collection that contains objects of any unknown type that extends the type of our list. That's good because it means you can addAlla List<Integer>to a List<Number>, but it makes our type inference trickier.

这意味着addAll接受任何包含任何未知类型对象的集合,这些对象扩展了我们的list. 这很好,因为这意味着您可以addAllaList<Integer>到 a List<Number>,但它使我们的类型推断更加棘手。

In fact it makes the type-inference not work within the rules currently laid out by the JLS. In somesituations it could be argued that the rules couldbe extended to work, but the current rules imply don't do it.

事实上,它使类型推断在 JLS 当前制定的规则内不起作用。在某些情况下,可能会争辩说这些规则可以扩展到工作,但当前的规则暗示不要这样做。

回答by Kal

The explanation from the Type Inferencedocumentation seems to answer this question directly ( unless I'm missing something else ).

类型推断文档中的解释似乎直接回答了这个问题(除非我遗漏了其他东西)。

Java SE 7 and later support limited type inference for generic instance creation; you can only use type inference if the parameterized type of the constructor is obvious from the context. For example, the following example does not compile:

Java SE 7 及更高版本支持通用实例创建的有限类型推断;如果构造函数的参数化类型从上下文中显而易见,则只能使用类型推断。例如,以下示例无法编译:

List<String> list = new ArrayList<>();
list.add("A");

  // The following statement should fail since addAll expects
  // Collection<? extends String>

list.addAll(new ArrayList<>());

Note that the diamond often works in method calls; however, for greater clarity, it is suggested that you use the diamond primarily to initialize a variable where it is declared.

请注意,菱形通常在方法调用中起作用;但是,为了更清楚,建议您主要使用菱形来初始化声明变量的变量

In comparison, the following example compiles:

相比之下,以下示例编译:

// The following statements compile:

List<? extends String> list2 = new ArrayList<>();
list.addAll(list2);

回答by irreputable

When compiling a method invocation, javac needs to know the type of the arguments first, before determining which method signature matches them. So the method parameter type isn't known before the argument type is known.

在编译方法调用时,javac 需要首先知道参数的类型,然后才能确定哪个方法签名与它们匹配。所以在参数类型已知之前方法参数类型是未知的。

Maybe this can be improved; as of today, the type of the argument is independent of the context.

也许这可以改进;截至今天,论点的类型与上下文无关。