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?
:: 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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With