鉴于这个例子 泛型教程。
List<String> list = new ArrayList<>();
list.add("A");
// The following statement should fail since addAll expects
// Collection<? extends String>
list.addAll(new ArrayList<>());
为什么最后一行不能编译,它似乎应该编译。第一行使用非常相似的构造并且编译没有问题。
请详细解释。
鉴于这个例子 泛型教程。
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,否则所有这些都无法正常工作,因为 钻石 <>
仅在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
。这很好,因为这意味着你可以 addAll
一个 List<Integer>
到了 List<Number>
,但它使我们的类型推断更棘手。
实际上,它使类型推断不能在JLS当前规定的规则范围内工作。在 一些 情况可以说是规则 可以 延伸到工作,但目前的规则意味着不这样做。
来自的解释 类型推断 文档似乎直接回答了这个问题(除非我错过了其他的东西)。
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);
在编译方法调用时,javac首先需要知道参数的类型,然后才能确定哪个方法签名与它们匹配。因此,在知道参数类型之前,不知道方法参数类型。
也许这可以改善;截至今天,论证的类型与上下文无关。