Why java compiler doesn't discriminate methods with different types?

71 views Asked by At

I defined following methods.

class Some {

    void doSome(Consumer<? super Other> consumer) {
    }

    <T extends Collection<? super Other>> T doSome(T collection) {
        doSome(collection::add); // <<<<<<<<<<<<<<<<<<<
        return collection;
    }
}

Now the javac complains.

java: reference to getAttributes is ambiguous both method doSome(java.util.function.Consumer<? super Other>) in ... and method doSome(T) in ...Some match

Why javac couldn't discriminate Consumer and Collection?

2

There are 2 answers

0
Sweeper On BEST ANSWER

This is because collection::add is not pertinent to applicability when invoking doSome(T).

Specifically, it is this case:

If m is a generic method and the method invocation does not provide explicit type arguments, an explicitly typed lambda expression or an exact method reference expression for which the corresponding target type (as derived from the signature of m) is a type parameter of m.

m here is doSome, which is a generic method. collection:add is an exact method reference. Its target type is (T) -> boolean (a function that takes in a T and returns boolean). (T) -> boolean includes the type parameter T, so it is not pertinent to applicability.

"Pertinent to applicability" is Java's way of deciding what argument expressions to consider during overload resolution. It simply doesn't consider some arguments (those that are not pertinent to applicability), because that would make overload resolution too complicated.

Since collection::add is practically "ignored", doSome(T) is also an applicable method, in addition to doSome(Consumer), which is obviously applicable. Therefore, there is an overload resolution ambiguity.

I would just add a cast to fix this:

doSome((Consumer<? super Other>)collection::add);

A cast expression is pertinent to applicability.

See also this similar question which is about implicitly typed lambda expressions, which are also not pertinent to applicability.

0
Tushar Raj On

The issue here is related to the type inference in Java and the ambiguity that arises when trying to resolve the overloaded methods. In your class Some, you have two doSome methods, and the type inference system is having difficulty determining which method to choose based on the provided arguments.

The ambiguity arises from the fact that both methods could potentially match the argument type you are providing, and the compiler is unable to make a clear decision.

To address this issue, you can explicitly specify the type parameter when invoking the doSome method with a lambda expression. This will help the compiler in selecting the correct method. Here's an example:

    Some someInstance = new Some();
someInstance.doSome((Consumer<Other>) other -> {
    // Your consumer logic here
});

// Or, if you are using the collection method
List<Other> otherList = someInstance.doSome(new ArrayList<>());

By explicitly specifying the type parameter in the lambda expression, you provide a clear indication to the compiler, helping it resolve the ambiguity between the two overloaded methods.