Java中有歧义的方法 8、为什么?

Ambiguous method in Java 8, why?

public static void main(String... args){
    then(bar()); // Compilation Error
}

public static <E extends Exception> E bar() {
    return null;
}

public static void then(Throwable actual) { }

public static void then(CharSequence actual) { }

编译结果(来自命令行javac Ambiguous.java

Ambiguous.java:4: error: reference to then is ambiguous
        then(bar());
        ^
  both method then(Throwable) in Ambiguous and method then(CharSequence) in Ambiguous match
1 error

为什么这个方法有歧义?此代码在 Java 7!

下编译成功

将方法栏更改为:

public static <E extends Float> E bar() {
    return null;
}

编译没有任何问题,但在 IntelliJ Idea 中报告为错误(无法解析方法 then(java.lang.FLoat))。

此代码在 Java 7 - javac -source 1.7 Ambiguous.java:

下失败
Ambiguous.java:4: error: no suitable method found for then(Float)
        then(bar());
        ^
    method Ambiguous.then(Throwable) is not applicable
      (argument mismatch; Float cannot be converted to Throwable)
    method Ambiguous.then(CharSequence) is not applicable
      (argument mismatch; Float cannot be converted to CharSequence)
1 error

Java版本

java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b25)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

考虑以下 class:

public class Foo extends Exception implements CharSequence {
    //...
}

class Foo 同时实现了 ThrowableCharSequence。因此,如果 E 设置为此实例,Java 编译器不知道要调用哪个方法。

Java7可能没有问题的原因是泛型的实现较少。如果您自己不提供 E(例如 (Foo) bar()),Java 将退回到 E 的基本版本,即 implements ExceptionE 因此仅被认为是 Exception.

的一个实例

Java8, the type inference is improved中,E的类型现在是从then()调用的参数派生的,换句话说,编译器首先查看可能的类型then()需要,问题是它们都是有效的选择。所以在那种情况下它变得模棱两可。


概念验证

现在我们将稍微修改您的代码并展示如何解决不明确的调用:

假设我们将代码修改为:

public class Main {
    public static void main(String... args){
        then(bar()); // Compilation Error
    }
    public static <E extends Exception> E bar() {
        return null;
    }
    public static void then(CharSequence actual) {
        System.out.println("char");
    }
}

如果你运行这个在Java8,是没有问题的(它打印char),因为Java8 简单地假设存在这样的 class Foo(它为它创建了某种从两者派生的 "inner" 类型)。

运行 Java7 中的这个会产生问题:

/MyClass.java:18: error: method then in class MyClass cannot be applied to given types;
    then(bar()); // Compilation Error
    ^
  required: CharSequence
  found: Exception
  reason: actual argument Exception cannot be converted to CharSequence by method invocation conversion
1 error

它在 Exception 上进行了回退,但找不到可以处理它的类型。

如果你运行原代码在Java8,会因为调用不明确而出错,如果你运行它然而,在 Java7 中,它将使用 Throwable 方法。


简而言之:编译器旨在 "guess" Java8 中的 E 是什么,而 Java7 中最保守的类型是已选.