Java 8 中对类型推断规则进行了重大修改;最显着的是目标类型推断得到了很大改进。因此,在 Java 8 之前,方法参数站点没有收到任何推断,默认为 Object,而在 Java 8 中,会推断出最具体的适用类型,在本例中为 String。 Java 8 的 JLS 引入了新的章节第 18 章类型推断 https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.htmlJava 7 的 JLS 中缺少这一点。
JDK 1.8 的早期版本(直到 1.8.0_25)存在与重载方法解析相关的错误,当编译器成功编译代码时,根据 JLS 应产生歧义错误为什么这个方法重载不明确? https://stackoverflow.com/questions/23020493/why-is-this-method-overloading-ambiguous正如 Marco13 在评论中指出的那样
JLS 的这一部分可能是最复杂的部分
其中解释了 JDK 1.8 早期版本中的错误以及您看到的兼容性问题。
如 Java 教程中的示例所示(类型推断 https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html)
考虑以下方法:
void processStringList(List<String> stringList) {
// process stringList
}
假设您想使用空列表调用 processStringList 方法。在 Java SE 7 中,以下语句无法编译:
processStringList(Collections.emptyList());
Java SE 7 编译器生成类似于以下内容的错误消息:
List<Object> cannot be converted to List<String>
编译器需要类型参数 T 的值,因此它以值 Object 开头。因此,Collections.emptyList 的调用返回 List 类型的值,该值与 processStringList 方法不兼容。因此,在 Java SE 7 中,您必须指定类型参数的值,如下所示:
processStringList(Collections.<String>emptyList());
这在 Java SE 8 中不再是必需的。目标类型的概念已扩展为包括方法参数,例如方法 processStringList 的参数。在这种情况下,processStringList 需要一个 List 类型的参数
Collections.emptyList()
是一个类似于的通用方法get()
问题中的方法。在 Java 7 中print(String string)
method 甚至不适用于方法调用,因此它不参与重载决策过程。而在 Java 8 中,这两种方法都适用。
这种不兼容性值得一提JDK 8 兼容性指南 http://www.oracle.com/technetwork/java/javase/8-compatibility-guide-2156366.html.
您可以查看我对与重载方法解析相关的类似问题的回答Java 8 三元条件和未装箱原语的方法重载歧义 https://stackoverflow.com/questions/30130720/method-overload-ambiguity-with-java-8-ternary-conditional-and-unboxed-primitives/30137369#30137369
根据JLS 15.12.2.5 选择最具体的方法 https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5:
如果多个成员方法既可访问又适用于一个
方法调用时,需要选择一个来提供
运行时方法调度的描述符。 Java编程
语言使用选择最具体方法的规则。
Then:
一种适用的方法 m1 比另一种适用的方法更具体
方法 m2,用于使用参数表达式 e1, ..., ek, if 进行调用
以下任何一项为真:
m2 是通用的,并且 m1 被推断为比 m2 更具体
参数表达式 e1, ..., ek 由 §18.5.4 确定。
m2不是通用的,m1和m2可以严格或宽松地适用
调用,其中 m1 具有形式参数类型 S1、...、Sn 和 m2
具有形式参数类型 T1, ..., Tn,类型 Si 更具体
对于所有 i (1 ≤ i ≤ n, n = k),参数 ei 比 Ti 更重要。
m2 不是通用的,m1 和 m2 通过变量数量适用
调用,其中 m1 的前 k 个变量参数类型
分别是 S1, ..., Sk 和 m2 的前 k 个可变参数类型
是 T1, ..., Tk,对于参数 ei,类型 Si 比 Ti 更具体
对于所有 i (1 ≤ i ≤ k)。另外,如果 m2 有 k+1 个参数,则
m1 的第 k+1 个变量参数类型是
m2的第k+1个可变参数类型。
上述条件是一种方法可能比另一种方法更具体的唯一情况。
对于任何表达式,如果 S <: t s>
三个选项中的第二个符合我们的情况。自从String
是一个子类型Object
(String <: Object
)更具体。因此该方法本身是更具体。继JLS之后,这个方法也是严格更具体 and 最具体的并由编译器选择。