Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warning: [overloads] method m1 is potentially ambiguous with method 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) { }
}

When I compile this with javac -Xlint Test.java I get a couple of warnings:

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

If I change Consumer to Supplier the warnings disappear. This program is warning free:

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) { }
}

Why is that? What does this warning mean? How are these methods ambiguous? Is it safe to suppress the warning?

like image 251
John Kugelman Avatar asked Mar 19 '15 02:03

John Kugelman


1 Answers

These warnings occur because of the fun intersection among overload resolution, target typing, and type inference. The compiler is thinking ahead a bit for you and is warning you because most lambdas are written without explicitly declared types. For example, consider this call:

    test(1, i -> { });

What is the type of i? The compiler can't infer it until it's completed overload resolution... but the value 1 matches all four of the overloads. Whichever overload is chosen would influence the target type of the second argument, which in turn would affect the type that's inferred for i. There really isn't enough information here for the compiler to decide which method to call, so this line will actually result in a compile-time error:

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

(Interestingly, it mentions the float and double overloads, but if you comment one of these out, you get the same error with respect to the long overload.)

One could imagine a policy where the compiler completed overload resolution using the most-specific rule, thereby choosing the overload with the int arg. It would then have a definite target type to apply to the lambda. The compiler designers felt that this was too subtle, and that there would be cases where programmers would be surprised about which overload ended up being called. Instead of compiling programs in a possibly unexpected way, they felt it was safer to make this an error and force the programmer to disambiguate it.

The compiler is issuing warnings at the method declarations to indicate that the likely code a programmer would write to call one of these methods (as shown above) will result in a compile-time error.

To disambiguate the call, one would instead have to write

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

or declare some other explicit type for the i parameter. Another way is to add a cast before the lambda:

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

but this is arguably worse. You probably don't want callers of your API to have to wrestle with this kind of thing at every point of call.

These warnings don't occur for the Supplier case, because the type of a Supplier can be determined via local reasoning, without any type inference.

You'll probably want to rethink the way you're putting this API together. If you really want methods with those argument types, you might do well to rename the methods testInt, testLong, etc. and avoid overloading entirely. Note that the Java SE APIs have done this in similar cases, such as comparingInt, comparingLong, and comparingDouble on Comparator; and also mapToInt, mapToLong, and mapToDouble on Stream.

like image 140
Stuart Marks Avatar answered Oct 19 '22 17:10

Stuart Marks