11

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

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

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

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

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

请详细解释。

4

3 回答 3

14

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

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

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

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

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

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

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

在这条线上:

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

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

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

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

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

于 2011-09-26T13:49:33.590 回答
1

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

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);
于 2011-09-26T13:48:33.400 回答
0

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

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

于 2011-09-26T20:58:32.970 回答