Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid method reference for overloaded method with different arities

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
like image 251
nosid Avatar asked Apr 08 '14 18:04

nosid


1 Answers

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.

like image 122
Scott Avatar answered Oct 26 '22 03:10

Scott