When trying to compile the expression Comparator.comparing(String::toLowerCase)
, the Java Compiler returns an error. See the following question for more information:
Why Comparator.comparing doesn't work with String::toLowerCase method reference?
I have tried to reduce the problem as much as possible. In particular, I have removed almost all dependencies to other classes. The main method contains two method invocations. The first statement compiles without errors, whereas the second statement produces an error.
interface Fun<T, R> { R apply(T t); }
public final class Foo {
public static void main(String... args) {
invoke(Foo::bar); // OK
invoke(Foo::baz); // ERROR
}
private static <T, U> void invoke(Fun<T, U> f) { }
private String bar() { return null; }
private String baz() { return null; }
private String baz(Integer i, Integer j) { return null; }
}
This is strange, because the second baz method shouldn't be applicable in this context, due to the mismatch in the number of parameters. I have taken a look at the JLS8 (15.13). However, it was no help, because the rules for method references are quite complex.
Q: Why is there a compilation error for the second case? Should there really be a compilation error according to the JLS? According to some comments on the other question, there is no compilation error in Netbeans.
For reference, I am using the JDK8 version 1.8.0-b132. If a compile the program on the command line, the compiler shows the following error message:
$ /opt/jdk8/bin/javac Foo.java
Foo.java:6: error: incompatible types: cannot infer type-variable(s) T,U
invoke(Foo::baz); // ERROR
^
(argument mismatch; invalid method reference
no suitable method found for baz(Object)
method Foo.baz() is not applicable
(actual and formal argument lists differ in length)
method Foo.baz(Integer,Integer) is not applicable
(actual and formal argument lists differ in length))
where T,U are type-variables:
T extends Object declared in method <T,U>invoke(Fun<T,U>)
U extends Object declared in method <T,U>invoke(Fun<T,U>)
Foo.java:6: error: invalid method reference
invoke(Foo::baz); // ERROR
^
non-static method baz() cannot be referenced from a static context
2 errors
JLS8 (15.13) is confusing but it does show examples similar to yours stating their are ambiguities in searching that it cannot resolve.
For your example Intellij says invoke(Foo::baz);
is a Cyclic inference which I think has more to do with the combination of invoke
needing to inferred a type as well as Foo::baz
.
This can be resolved by giving a type to the invoke
function, similar to JSL (15.13.1 examples)
The search is smart enough to ignore ambiguities in which all the applicable methods (from both searches) are instance methods:
Foo.<Foo,String>invoke(Foo::baz);
-- equivalent to, I want to use the void
method of Foo
that returns a String
aka String baz()
interface Fun<T, R> { R apply(T t); }
interface Fun2<T,U,R> { R apply(T t, U u); }
public final class Foo {
public static void main(String... args) {
invoke(Foo::bar); // OK
Foo.<Foo,String>invoke(Foo::baz); // NO ERROR
Fun2<Foo, Integer, String> f2 = Foo::baz; // Overloaded method baz
}
private static <T, U> void invoke(Fun<T, U> f) { }
private String bar() { return null; }
private String baz() { return null; }
private String baz(Integer i) { return null; }
}
I do agree with you that baz(Integer i)
is not a valid argument to invoke
with out making it static or from an instance of Foo
. I guess the search algorithm just quits if the method is overloaded and it is trying to infer the type. Because it does work with just a single method signature.
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