Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java cannot find correct overloaded method with double colon operator

When using the double colon operator to refer to an overloaded method, Java does not seem to be able to determine the correct method to use. Consider this example:

public class A {
    private void setter(final Number value) { }
    private void setter(final Optional<Number> value) { }
    private void setter2(final Optional<Number> value) { }

    private <T> void useSetter(final Consumer<Optional<T>> a) { }

    private void callMethod() {
        useSetter(this::setter); // Error here
        useSetter(this::setter2);
    }
}

The first call to useSetter does not compile and gives the following errors:

Cannot infer type argument(s) for <T> useSetter(Consumer<Optional<T>>)
The type A does not define setter(Optional<Object>) that is applicable here

However, the second call compiles just fine, which means that the problem is in the overloading of setter. Only one of the setter overloads is applicable, so I don't understand why this doesn't work.

It is possible to get around this by using a lambda that specifies the parameter type, but that's a lot more verbose.

useSetter((final Optional<Number> v) -> setter(v));

Is there a better way to handle this situation or am I stuck working around this strange quirk?

like image 387
Sam Avatar asked Jul 26 '17 13:07

Sam


People also ask

What is use of :: in Java 8?

:: is a new operator included in Java 8 that is used to refer to a method of an existing class. You can refer to static methods and non-static methods of a class. The only prerequisite for referring to a method is that the method exists in a functional interface, which must be compatible with the method reference.

How is a call to an overloaded method resolved in Java?

The process of compiler trying to resolve the method call from given overloaded method definitions is called overload resolution. If the compiler can not find the exact match it looks for the closest match by using upcasts only (downcasts are never done). As expected the output is 10.


1 Answers

The capture for the compilation of your private <T> void useSetter(final Consumer<Optional<T>> a) { } method is Optional<Object>. The compiler is trying to tell you that it can't coerce the type to match any known captures.

Main.java:12: error: incompatible types: cannot infer type-variable(s) T
        useSetter(this::setter); // Error here
                 ^
    (argument mismatch; invalid method reference
      no suitable method found for setter(Optional<Object>)
          method A.setter(Number) is not applicable
            (argument mismatch; Optional<Object> cannot be converted to Number)
          method A.setter(Optional<Number>) is not applicable
            (argument mismatch; Optional<Object> cannot be converted to Optional<Number>))
  where T is a type-variable:
    T extends Object declared in method <T>useSetter(Consumer<Optional<T>>)

One solution is to create a bound using private <T> void setter(final Optional<? super T> value) { } for a generic parameterized optional type. The other option is to imply some coercion capability to the compiler private void setter(final Optional<? super Number> value) { }.

class A<T> {
    private void setter(final Number value) { }
    private <T> void setter(final Optional<? super T> value) { }
    private void setter2(final Optional<Number> value) { }

    private <T> void useSetter(final Consumer<Optional<T>> a) { }

    private void callMethod() {
        useSetter(this::setter); // no more error
        useSetter(this::setter2);
    }

    public static void main(String [] args){

    }
}

class B {
    private void setter(final Number value) { }
    private void setter(final Optional<? super Number> value) { }
    private void setter2(final Optional<Number> value) { }

    private <T> void useSetter(final Consumer<Optional<T>> a) { }

    private void callMethod() {
        useSetter(this::setter); // no more error
        useSetter(this::setter2);
    }

    public static void main(String [] args){

    }
}

You can check out the ideone here.

Neither option is perfect as it will introduce some fungibility to your code by allowing the Optional<Object> to be passed, however if you avoid using raw types directly you should be fine.

like image 92
Michael Hibay Avatar answered Nov 16 '22 01:11

Michael Hibay