java – 警告:[overloads]方法m1与方法m2有潜在的不确定性

前端之家收集整理的这篇文章主要介绍了java – 警告:[overloads]方法m1与方法m2有潜在的不确定性前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
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) { }
}

这是为什么?这个警告是什么意思?这些方法如何模糊?抑制警告是否安全?

解决方法

这些警告是由于重载分辨率,目标打字和类型推断之间有趣的交集而发生的.编译器为您提前思考,并且警告您,因为大多数lambdas都没有明确声明的类型.例如,考虑这个电话:
test(1,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重载,但是如果你对其中的一个发表评论,你会得到相同的长时间重载错误.)

可以想象一个策略,编译器使用最具体的规则完成重载解析,从而选择使用int arg的重载.那么它将有一个明确的目标类型来应用于lambda.编译器设计者觉得这太微妙了,而且有些情况下程序员会惊讶于哪个重载最终被调用.而不是以可能的意想不到的方式编译程序,他们认为使这个错误更加安全,并强制程序员消除歧义.

编译器在方法声明中发出警告,以指示程序员可能编写的代码调用其中一个方法(如上所示)将导致编译时错误.

要消除这个电话的歧义,只好写一个

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

或者为i参数声明一些其他显式类型.另一种方法是在lambda之前添加一个cast:

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

但这可以说更糟.您可能不希望您的API的呼叫者必须在每个呼叫点与这种事情搏斗.

供应商案例不会出现这些警告,因为供应商的类型可以通过本地推理来确定,而不需要任何类型的推断.

你可能想要重新思考将这个API放在一起的方式.如果您真的想要使用这些参数类型的方法,那么可以重命名方法testInt,testLong等,并避免完全重载.请注意,在类似的情况下,Java SE API已经做到了这一点,比如比较比较,比较长和比较比较比较;还可以在Stream上映射ToInt,mapToLong和mapToDouble.

猜你在找的Java相关文章