java - 警告 : [overloads] method m1 is potentially ambiguous with method m2

标签 java java-8 compiler-warnings overloading functional-interface

import java.util.function.*;

class Test { 
    void test(int    foo, Consumer<Integer> bar) { }
    void test(long   foo, Consumer<Long>    bar) { }
    void test(float  foo, Consumer<Float>   bar) { }
    void test(double foo, Consumer<Double>  bar) { }
}

当我用 javac -Xlint Test.java 编译它时,我收到了一些警告:

Test.java:4: warning: [overloads] test(int,Consumer<Integer>) in Test is potentially ambiguous with test(long,Consumer<Long>) in Test
    void test(int    foo, Consumer<Integer> bar) { }
         ^
Test.java:6: warning: [overloads] test(float,Consumer<Float>) in Test is potentially ambiguous with test(double,Consumer<Double>) in Test
    void test(float  foo, Consumer<Float>   bar) { }
         ^
2 warnings

如果我将 Consumer 更改为 Supplier,警告就会消失。该程序没有警告:

import java.util.function.*;

class Test { 
    void test(int    foo, Supplier<Integer> bar) { }
    void test(long   foo, Supplier<Long>    bar) { }
    void test(float  foo, Supplier<Float>   bar) { }
    void test(double foo, Supplier<Double>  bar) { }
}

这是为什么呢?这个警告是什么意思?这些方法是如何模棱两可的?抑制警告是否安全?

最佳答案

这些警告的出现是因为重载解析、目标类型和类型推断之间有趣的交集。编译器会提前为您考虑并警告您,因为大多数 lambda 都是在没有明确声明类型的情况下编写的。例如,考虑这个调用:

    test(1, i -> { });

i 的类型是什么?编译器在完成重载解析之前无法推断它...但是值 1 匹配所有四个重载。无论选择哪个重载都会影响第二个参数的目标类型,这反过来会影响为 i 推断的类型。这里确实没有足够的信息让编译器决定调用哪个方法,所以这一行实际上会导致编译时错误:

    error: reference to test is ambiguous
           both method test(float,Consumer<Float>) in Test and
           method test(double,Consumer<Double>) in Test match

(有趣的是,它提到了 floatdouble 重载,但是如果你注释掉其中之一,你会得到与 long 相同的错误 重载。)

可以想象一种策略,编译器使用最具体的规则完成重载解析,从而选择带有 int arg 的重载。然后它将有一个明确的目标类型应用于 lambda。编译器设计者觉得这太微妙了,而且在某些情况下,程序员会对最终调用的重载感到惊讶。与其以可能出乎意料的方式编译程序,他们觉得将其作为错误并强制程序员消除歧义更为安全。

编译器在方法声明中发出警告,表明程序员编写的代码可能会调用这些方法之一(如上所示)将导致编译时错误。

为了消除调用的歧义,必须写成

    test(1, (Integer i) -> { });

或者为 i 参数声明一些其他显式类型。另一种方法是在 lambda 之前添加强制转换:

    test(1, (Consumer<Integer>)i -> { });

但这可以说更糟。您可能不希望您的 API 的调用者在每次调用时都必须处理这种事情。

Supplier 情况下不会出现这些警告,因为 Supplier 的类型可以通过本地推理确定,无需任何类型推断。

您可能需要重新考虑将此 API 组合在一起的方式。如果您确实需要具有这些参数类型的方法,最好将方法重命名为 testInttestLong 等,并避免完全重载。请注意,Java SE API 在类似情况下已完成此操作,例如 Comparator 上的 comparingIntcomparingLongcomparingDouble >;以及 Stream 上的 mapToIntmapToLongmapToDouble

关于java - 警告 : [overloads] method m1 is potentially ambiguous with method m2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29136053/

相关文章:

c++ - 我应该使用哪些编译标志来避免运行时错误

haskell - SPECIALIZE 编译用语的误解

java - 没有打包资源的 LibGDX Gradle : Export . jar

java - 编译运行Java时如何设置TextMate使用的CLASSPATH

java - 使用 Java 8 Streams API 列出迭代和设置值

Eclipse、lambda 和 Java 8 模板

java - 有什么方法可以跟踪或监听用户点击浏览器中的任何链接,或任何聊天信使应用程序甚至短信吗?

java - SmartGWT 中的多列网格过滤器

java - 如何在嵌入式 Grizzly Jersey 应用程序的主方法中使用 HK2 注入(inject)对象

c++ - 为什么返回局部变量或临时地址只是警告而不是错误?