类型推断的规则在Java 8中得到了重大的改革。最值得注意的是,目标类型推断得到了很大的改进。因此,在Java 8之前,方法参数站点没有收到任何推断,默认为Object,而在Java 8中,最具体的适用类型是推断出来的,在本例中为String。Java 8 的 JLS 引入了新的第 18 章。Java 7 的 JLS 中缺少的类型推理。
早期版本的JDK 1.8(直到1.8.0_25)在编译器成功编译代码时有一个与重载方法解析相关的错误,根据JLS应该产生歧义错误为什么这种方法重载不明确?正如Marco13在评论中指出的那样
JLS的这一部分可能是最复杂的一个
这解释了早期版本的JDK 1.8中的错误以及您看到的兼容性问题。
如 Java Tutoral(类型推断)中的示例所示)
请考虑以下方法:
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()
是一种通用方法,类似于问题中的方法。在Java 7中,print(String string)
方法甚至不适用于方法调用,因此它不参与重载解析过程。而在Java 8中,这两种方法都适用。get()
这种不兼容性值得在 JDK 8 的兼容性指南中提及。
您可以查看我对与重载方法解析相关的类似问题的答案 Java 8 三元条件和未装箱基元的方法重载歧义
根据 JLS 15.12.2.5 选择最具体的方法:
如果多个成员方法既可访问又适用于方法调用,则需要选择一个成员方法来为运行时方法调度提供描述符。Java 编程语言使用选择最具体方法的规则。
然后:
一个适用的方法 m1 比另一个适用的方法 m2 更具体,用于使用参数表达式 e1, ..., ek 的调用(如果满足以下任一条件):
m2 是通用的,对于参数表达式 e1, ..., ek,§18.5.4,m1 被推断为比 m2 更具体。
m2 不是泛型的,m1 和 m2 可以通过严格或松散调用来应用,其中 m1 具有形式参数类型 S1, ...,Sn 和 m2 具有形式参数类型 T1, ..., Tn,对于所有 i 的参数 ei,类型 Si 比 Ti 更具体(1 ≤ i ≤ n, n = k)。
m2 不是泛型的,m1 和 m2 通过变量 arity 调用适用,其中 m1 的第一个 k 变量 arity 参数类型是 S1, ...,Sk 和 m2 的第一个 k 个变量 arity 参数类型是 T1, ..., Tk,对于所有 i 的参数 ei,Si 类型比 Ti 更具体(1 ≤ i ≤ k)。此外,如果 m2 具有 k+1 个参数,则 m1 的第 k+1 个变量 arity 参数类型是 m2 的第 k+1'个变量 arity 参数类型的子类型。
上述条件是一种方法可能比另一种方法更具体的唯一情况。
对于任何表达式,如果 S <:T (§4.10),则类型 S 比类型 T 更具体。
三个选项中的第二个与我们的情况相匹配。由于 是 () 的子类型,因此它更具体。因此,方法本身更具体。遵循JLS,此方法也严格地更具体,最具体,并由编译器选择。String
Object
String <: Object