为什么Java8泛型类型推断选择这个重载?
考虑以下计划:为什么Java8泛型类型推断选择这个重载?,java,generics,java-8,language-lawyer,Java,Generics,Java 8,Language Lawyer,考虑以下计划: 公共类泛型推理{ 公共静态void main(字符串[]args){ 打印(新的SillyGenericWrapper().get()); } 私有静态无效打印(对象){ System.out.println(“对象”); } 私有静态无效打印(字符串){ System.out.println(“字符串”); } 公共静态类SillyGenericWrapper{ 公共部门得不到{ 返回null; } } } 它在Java8下打印“字符串”,在Java7下打印“对象” 我本以为
公共类泛型推理{
公共静态void main(字符串[]args){
打印(新的SillyGenericWrapper().get());
}
私有静态无效打印(对象){
System.out.println(“对象”);
}
私有静态无效打印(字符串){
System.out.println(“字符串”);
}
公共静态类SillyGenericWrapper{
公共部门得不到{
返回null;
}
}
}
它在Java8下打印“字符串”,在Java7下打印“对象”
我本以为这在Java8中是不明确的,因为两个重载方法都匹配。为什么编译器在之后选择打印(字符串)
不管是否合理,这会破坏向后兼容性,并且在编译时无法检测到更改。在升级到Java8之后,代码的行为就不一样了
注意:SillyGenericWrapper
之所以命名为“傻”是有原因的。我试图理解为什么编译器的行为如此,不要告诉我愚蠢的包装是一个糟糕的设计
更新:我还尝试在Java8下编译和运行该示例,但使用的是Java7语言级别。该行为与Java7一致。这是意料之中的,但我仍然觉得需要验证。我使用Java1.8.0\u40运行它,得到了“Object” 如果要运行以下代码:
public class GenericTypeInference {
private static final String fmt = "%24s: %s%n";
public static void main(String[] args) {
print(new SillyGenericWrapper().get());
Method[] allMethods = SillyGenericWrapper.class.getDeclaredMethods();
for (Method m : allMethods) {
System.out.format("%s%n", m.toGenericString());
System.out.format(fmt, "ReturnType", m.getReturnType());
System.out.format(fmt, "GenericReturnType", m.getGenericReturnType());
}
private static void print(Object object) {
System.out.println("Object");
}
private static void print(String string) {
System.out.println("String");
}
public static class SillyGenericWrapper {
public <T> T get() {
return null;
}
}
}
公共类泛型推理{
私有静态最终字符串fmt=“%24s:%s%n”;
公共静态void main(字符串[]args){
打印(新的SillyGenericWrapper().get());
方法[]allMethods=SillyGenericWrapper.class.getDeclaredMethods();
对于(方法m:所有方法){
System.out.format(“%s%n”,m.toGenericString());
System.out.format(fmt,“ReturnType”,m.getReturnType());
格式(fmt,“GenericReturnType”,m.getGenericReturnType());
}
私有静态无效打印(对象){
System.out.println(“对象”);
}
私有静态无效打印(字符串){
System.out.println(“字符串”);
}
公共静态类SillyGenericWrapper{
公共部门得不到{
返回null;
}
}
}
您将看到您得到:
对象公共T
com.xxx.GenericTypeInference$SillyGenericWrapper.get()
ReturnType:类java.lang.Object
GenericReturnType:T
这解释了为什么使用对象重载的方法而不是字符串重载的方法。首先,它与重写无关,但必须处理重载 Jls.提供了大量有关编译器如何准确选择重载方法的信息 在编译时选择最具体的方法;它的描述符 确定在运行时实际执行的方法 所以当调用
print(new SillyGenericWrapper().get());
编译器选择String
version而不是Object
,因为采用String
的print
方法比采用Object
的方法更具体。如果存在Integer
而不是String
,则会选择它
此外,如果要调用将对象
作为参数的方法,则可以将返回值分配给对象
类型的参数,例如
public class GenericTypeInference {
public static void main(String[] args) {
final SillyGenericWrapper sillyGenericWrapper = new SillyGenericWrapper();
final Object o = sillyGenericWrapper.get();
print(o);
print(sillyGenericWrapper.get());
}
private static void print(Object object) {
System.out.println("Object");
}
private static void print(Integer integer) {
System.out.println("Integer");
}
public static class SillyGenericWrapper {
public <T> T get() {
return null;
}
}
}
当假设你有两个有效的方法定义可以重载时,情况开始变得有趣起来
private static void print(Integer integer) {
System.out.println("Integer");
}
private static void print(String integer) {
System.out.println("String");
}
现在如果你调用
print(sillyGenericWrapper.get());
编译器将有两个有效的方法定义可供选择,因此您将得到编译错误,因为它不能优先选择一个方法。类型推断规则在Java 8中得到了重大改进;最显著的是目标类型推断得到了很大改进。因此,在Java 8之前,方法rgument站点没有收到任何推断,默认为Object,在Java 8中推断出最具体的适用类型,在本例中为String。Java 8的JLS引入了Java 7的JLS中缺少的新章节 JDK1.8的早期版本(直到1.8.0_25)在编译器成功编译代码时出现了与重载方法解析相关的错误,根据JLS,正如Marco13在评论中指出的那样,该代码应该会产生歧义错误 JLS的这一部分可能是最复杂的部分 这解释了JDK1.8早期版本中的错误,以及您看到的兼容性问题
如Java教程()中的示例所示 考虑以下方法: Java SE 7编译器生成类似以下内容的错误消息:
列表无法转换为列表
编译器需要类型参数T的值,因此它以值对象开始。因此,调用Collections.emptyList返回类型List的值,该值与方法processStringList不兼容。因此,在Java SE 7中,必须按如下方式指定类型参数的值:
processStringList(Collections.emptyList());
这在Java SE 8中不再必要。什么是目标类型的概念已扩展为包括方法参数,例如方法processStringList的参数。在这种情况下,processStringList需要类型为List的参数
Collections.emptyList()
是一种通用方法,类似于问题中的get()
方法。在Java 7中print(String)
方法甚至不适用于方法调用,因此它不参与重载解析过程。而在Java 8中,这两种方法都适用
这种不相容性值得一提
您可以查看我关于重载方法解析的类似问题的答案 根据: 如果超过1米
print(sillyGenericWrapper.get());
void processStringList(List<String> stringList) {
// process stringList
}
processStringList(Collections.emptyList());
List<Object> cannot be converted to List<String>
processStringList(Collections.<String>emptyList());
print( (Object)new SillyGenericWrapper().get() );