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
(有趣的是,它提到了 float
和 double
重载,但是如果你注释掉其中之一,你会得到与 long 相同的错误
重载。)
可以想象一种策略,编译器使用最具体的规则完成重载解析,从而选择带有 int
arg 的重载。然后它将有一个明确的目标类型应用于 lambda。编译器设计者觉得这太微妙了,而且在某些情况下,程序员会对最终调用的重载感到惊讶。与其以可能出乎意料的方式编译程序,他们觉得将其作为错误并强制程序员消除歧义更为安全。
编译器在方法声明中发出警告,表明程序员编写的代码可能会调用这些方法之一(如上所示)将导致编译时错误。
为了消除调用的歧义,必须写成
test(1, (Integer i) -> { });
或者为 i
参数声明一些其他显式类型。另一种方法是在 lambda 之前添加强制转换:
test(1, (Consumer<Integer>)i -> { });
但这可以说更糟。您可能不希望您的 API 的调用者在每次调用时都必须处理这种事情。
Supplier
情况下不会出现这些警告,因为 Supplier 的类型可以通过本地推理确定,无需任何类型推断。
您可能需要重新考虑将此 API 组合在一起的方式。如果您确实需要具有这些参数类型的方法,最好将方法重命名为 testInt
、testLong
等,并避免完全重载。请注意,Java SE API 在类似情况下已完成此操作,例如 Comparator
上的 comparingInt
、comparingLong
和 comparingDouble
>;以及 Stream
上的 mapToInt
、mapToLong
和 mapToDouble
。
关于java - 警告 : [overloads] method m1 is potentially ambiguous with method m2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29136053/