Java 8中的方法模棱两可,为什么?

2022-09-02 21:44:10
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)

答案 1

请考虑以下类:

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

该类同时实现 和 。因此,如果设置为此实例,Java 编译器不知道要调用哪个方法。FooThrowableCharSequenceE

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

Java8中,类型推断得到了改进,类型现在派生自参数调用,换句话说,编译器首先要看一下可能的类型需要什么,问题是它们都是有效的选择。因此,在这种情况下,它变得模棱两可。Ethen()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中运行这个,没有问题(它打印),因为Java8只是假设有这样的类(它为它创建了某种“内部”类型,从两者派生出来)。charFoo

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


简而言之:编译器旨在“猜测”Java8中的E是什么,而在Java7中选择了最保守的类型。


答案 2

推荐