简体   繁体   English

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

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

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. 请详细解释。

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 parts of 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 major enhancement (note: I don't encourage 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 seems to 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> Collection<? extends E> . Collection<? extends E>

It means that addAll accepts any collection that contains objects of any unknown type that extends the type of our list . 这意味着addAll接受任何包含任何未知类型的对象的集合,这些对象扩展了list的类型。 That's good because it means you can addAll a List<Integer> to a List<Number> , but it makes our type inference trickier. 这是好事,因为它意味着你可以addAll一个List<Integer>List<Number> ,但它使我们的类型推断棘手。

In fact it makes the type-inference not work within the rules currently laid out by the JLS. 实际上,它使类型推断不适用于JLS当前规定的规则。 In some situations it could be argued that the rules could be extended to work, but the current rules imply don't do it. 某些情况下,可以认为规则可以扩展到工作,但目前的规则意味着不这样做。

The explanation from the Type Inference documentation seems to answer this question directly ( unless I'm missing something else ). Type Inference文档中的解释似乎直接回答了这个问题(除非我遗漏了其他内容)。

Java SE 7 and later support limited type inference for generic instance creation; Java SE 7及更高版本支持通用实例创建的有限类型推断; 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: 例如,以下示例不编译:

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);

When compiling a method invocation, javac needs to know the type of the arguments first, before determining which method signature matches them. 在编译方法调用时,javac首先需要知道参数的类型,然后才能确定哪个方法签名与它们匹配。 So the method parameter type isn't known before the argument type is known. 因此,在知道参数类型之前,不知道方法参数类型。

Maybe this can be improved; 也许这可以改善; as of today, the type of the argument is independent of the context. 截至今天,论证的类型与上下文无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM