【问题标题】:Why doesn't the diamond operator work within a addAll() call in Java 7?为什么钻石运算符在 Java 7 的 addAll() 调用中不起作用?
【发布时间】:2011-11-25 05:17:13
【问题描述】:

给出来自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<>());

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

请详细解释。

【问题讨论】:

  • "为什么以下不起作用?"有点弱。你到底期待什么?错误信息是什么? “它不起作用”的原因有很多。您应该更准确地回答问题以获得准确的答案。
  • 实际上,从 Java 7 开始,这应该可以工作 - diamond operator,也投票支持重新开放。
  • Pradeep,您使用的是 Java 7 吗?这是编译的先决条件。
  • @PradeepKumar:如果您使用 Java 7,那么 就是您的代码无法编译的原因:本教程是针对 Java 7。但是您引用的特定代码甚至无法在 Java 7 上编译(出于其他原因)。

标签: java generics java-7 diamond-operator


【解决方案1】:

首先:除非您使用的是 Java 7,否则所有这些都不起作用,因为 the diamond &lt;&gt; 仅在该 Java 版本中引入。

此外,此答案假设读者understands the basics of generics。如果您不了解,请阅读本教程的其他部分,并在您理解这些内容后回来。

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

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

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

在此示例中,差异并不大,但一旦您到达 Map&lt;String, ThreadLocal&lt;Collection&lt;Map&lt;String,String&gt;&gt;&gt;&gt;,它将是一个主要增强(注意:我鼓励实际使用这样的构造!)。

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

在这一行:

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

似乎是显而易见的。至少开发者知道类型应该是String

但是,查看Collection.addAll() 的定义,我们看到参数类型是Collection&lt;? extends E&gt;

这意味着addAll 接受任何包含任何未知类型对象的集合,这些对象扩展了我们的list 的类型。这很好,因为这意味着您可以将 addAllList&lt;Integer&gt; 转换为 List&lt;Number&gt;,但这会使我们的类型推断变得更加棘手。

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

【讨论】:

  • @joachim.感谢您的回答
【解决方案2】:

Type Inference 文档中的解释似乎直接回答了这个问题(除非我遗漏了其他内容)。

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

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

相比之下,下面的例子编译:

// The following statements compile:

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

【讨论】:

  • 本教程并没有真正解释为什么钻石在这里不起作用。它只是挥手说它“经常有效”。
  • @Kal。抱歉打扰你了。我不太明白。你能用简单的话解释一下吗。再次抱歉
  • 同意。也许 JLS 说了些什么。
  • 嗯.. 这对我来说似乎很明显。它明确指出 addAll 期望 Collection extends E> 你不能这样做,因为它可能是扩展列表的定义类型的未知类型,因此推理将不起作用。
  • @Kal:很容易说,在这种情况下,它可以简单地推断出类型 String,根据类型系统,这将是正确的,并且可以成功地允许通话。在我看来,这就足够了。这似乎是一个类似于旧的“为什么对象实例化不使用与方法调用相同的推理规则?”的限制。在 Java 5 和 Java 6 中(在 Java 7 中仍然如此)。
【解决方案3】:

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

也许这可以改进;到今天为止,参数的类型与上下文无关。

【讨论】:

    猜你喜欢
    • 2014-05-15
    • 1970-01-01
    • 1970-01-01
    • 2021-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-29
    • 1970-01-01
    相关资源
    最近更新 更多