Java 8:对 [方法] 的引用不明确

2022-09-01 08:55:28

有没有人理解为什么下面的代码在Java 7及以下版本中编译良好,但在Java 8中却失败了。

public static void main(String[] args) throws Exception {
    put(get("hello"));
}

public static <R> R get(String d) {
    return (R)d;
}

public static void put(Object o) {
    System.err.println("Object " + o);
}

public static void put(CharSequence c) {
    System.err.println("CharSequence " + c);
}

public static void put(char[] c) {
    System.err.println("char[] " + c);
}

get 方法具有泛型返回类型。在 JDK 7 及更低版本中,编译良好,并且选择了带有 Object 参数的 put 方法。在 JDK 8 中,这无法编译,这表明 put 方法不明确。

显然,JDK 8 跳过了 Object 参数方法,找到了最后两个子 Object 参数方法并抱怨它们(即,如果您使用其他参数类型添加另一个 put 方法,编译器将切换并抱怨新的最后两个方法)

这似乎是一个错误。


答案 1

您的问题是广义目标类型推理的副作用,这是Java 8中的改进。

什么是目标类型推理

让我们以您的示例方法为例,

public static <R> R get(String d) {
    return (R)d;
}

现在,在上面的方法中,编译器无法解析泛型参数,因为 没有带有 的参数。RR

因此,他们引入了一个名为 的概念,该概念允许根据赋值参数推断参数。Target-type Inference

所以,如果你这样做,

 String str = get("something"); // R is inferred as String here
 Number num = get("something"); // R is inferred as Number here

这在Java 7中运行良好。但以下情况并非如此

put(get("something");
static void Put(String str) {} //put method

因为类型推断仅适用于直接赋值。

如果没有直接赋值,则将泛型类型推断为 Object

因此,当您使用Java 7编译代码时,您的方法被调用而没有任何问题。put(Object)

他们在Java 8中做了什么

他们改进了类型推断,以从方法调用链接方法调用中推断类型。

有关它们的更多详细信息,请单击此处此处

所以现在,你可以直接调用,泛型类型将根据put()方法的参数推断出来put(get("something"))

但如您所知,方法和参数匹配。所以有模棱两可。put(Charsequence)put(char[])

修复?

只需告诉编译器你想要什么,

put(TestClass.<CharSequence>get("hello")); // This will call the put(CharSequence) method.

答案 2

看起来这是一个已知的不兼容性。

请参阅本文的“区域:工具/javac”部分还有这个错误

概要

在 JDK 7 中编译的以下带有警告的代码将无法在 JDK 8 中编译:

import java.util.List;
 
class SampleClass {
 
    static class Baz<T> {
        public static List<Baz<Object>> sampleMethod(Baz<Object> param) {
            return null;
        }
    }
 
    private static void bar(Baz arg) {
        Baz element = Baz.sampleMethod(arg).get(0);
    }
} 

在 JDK 8 中编译此代码会产生以下错误:

SampleClass.java:12: error:incompatible types: Object cannot be converted to Baz
    Baz element = Baz.sampleMethod(arg).get(0);
                                          
Note: SampleClass.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error 

在此示例中,原始类型被传递给 sampleMethod(Baz<Object>) 方法,该方法可通过子类型化应用(请参阅 JLS,Java SE 7 版,第 15.12.2.2 节)。

要使该方法适用,必须进行未经检查的转换,因此将擦除其返回类型(请参阅 JLS,Java SE 7 版,第 15.12.2.6 节)。在这种情况下,sampleMethod(Baz<Object>) 的返回类型是 java.util.List 而不是 java.util.List<Baz<Object>> 因此 get(int) 的返回类型是 Object,这与 Baz 的赋值不兼容。


推荐