【问题标题】:Ambiguous method in Java 8, why? [duplicate]Java 8 中的模棱两可的方法,为什么? [复制]
【发布时间】:2015-06-12 12:53:48
【问题描述】:
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)

【问题讨论】:

  • 如果我说我刚刚使用 jdk1.8.0_40 成功运行了您的代码,会有帮助吗?只需将其粘贴到一个新的 Idea 项目中,然后单击“运行”并导入 junit 注释。根据您看到的错误,第一个显然 -version with Float 在任何 java 版本上都绝对是错误的。
  • @marvin82:在try Java8 中失败了。
  • marvin82,它编译在java8下用Float很好,没有错误gist.github.com/mariuszs/0a82ca08f55499906ea9
  • 我的错,我覆盖了语言级别。很抱歉造成混乱。

标签: java generics java-8 java-7 ambiguous


【解决方案1】:

考虑以下类:

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

Foo 类实现了ThrowableCharSequence。所以如果E被设置为这个实例,Java编译器不知道调用哪个方法。

Java7 可能没有问题的原因是泛型的实现较少。如果您自己不提供E(例如(Foo) bar()),Java 将使用E 的基本版本,即implements Exception,因此E 仅被视为@ 的一个实例987654333@.

Java8type 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只是假设有这样的类Foo(它创建了某种“内部”类型因为它来源于两者)。

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方法。


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

【讨论】:

  • 那么,在这种情况下,我们应该避免将接口与类混用吗?
  • @MariuszS: 好吧,你可以明确表示(例如(Exception) bar() 你在谈论哪个E。但一般来说最大化(非finalclasses 和interface s 和 interfaces 和 interfaces 我猜是在找麻烦。
  • @MariuszS:不,一般来说,你应该避免声明像E bar() 这样的方法,它基本上说“调用者可以决定bar()”返回什么。由于唯一可以持有这个承诺的有效返回值是null,所以这样的方法没有意义。
  • @Holger:在 C# 中,您还可以添加 new() 约束。在这种情况下,您强制应该有一个可用的默认约束。在这种情况下,它变得有用。据我所知,这在 Java8 中是不可用的,但最终可以实现。
  • @CommuSoft: 但是当方法不明确的时候,C#怎么知道要实例化哪个类型呢?在 Java 8 中,您可以通过向bar() 添加Supplier&lt;E&gt; 参数来执行此操作,然后调用者可以使用then(bar(DesiredType::new)) 通过同时提供适当的Supplier 来消除歧义……
猜你喜欢
  • 2021-11-28
  • 1970-01-01
  • 2014-12-10
  • 1970-01-01
  • 1970-01-01
  • 2019-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多