Search code examples
javalambdajava-8method-referenceambiguity

Ambiguity in method references


Consider the following snippet:

public static void main(String[] args) {
        Function<String, String> function = String::toUpperCase;        //OK
//        Comparator<String> comparator = String::toUpperCase;          //Compilation error(makes sense, as String.toUpperCase(Locale locale) & String.toUpperCase() are not compatible)
        fun(String::toUpperCase);                                       // java: reference to fun is ambiguous
    }

    public static void fun(Function<String, String> function) { // String apply(String obj)
        System.out.println("Function");
    }

    public static void fun(Comparator<String> comparator) {     // int compare(String s1, String s2)
        System.out.println("Comparator");
    }

I'm failing to understand the reason behind ambiguity error for method invocation fun(String::toUpperCase).

As, both of the overloaded versions of String::toUpperCase themselves are not compatible with int compare(String s1, String s2) from the Comparator class, then how come the compiler complains about ambiguity in the first place?

Am I missing something here?


Solution

  • toUpperCase has an overload that takes no parameters, and another overload that takes one parameter (Locale).

    This makes the expression String::toUpperCase an inexact method reference expression. The expression could either refer to the no-argument overload, or the one-argument overload.

    Both of the two funs are determined to be "potentially applicable", specifically because of this clause:

    • A method reference expression is potentially compatible with a functional interface type T if, where the arity of the function type of T is n, there exists at least one potentially applicable method when the method reference expression targets the function type with arity n, and one of the following is true:

      • The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is either (i) static and supports arity n, or (ii) not static and supports arity n-1.
      • [irrelevant]

    String::toUpperCase is potentially compatible with Function<String, String>, because Function<String, String> has arity 1 (takes one parameter), and toUpperCase is non-static and has a no-argument overload.

    String::toUpperCase is potentially compatible with Comparator<String>, because Comparator<String> has arity 2, and toUpperCase is non-static and has a one-argument overload. Note that this step does not check the parameter types or return types at all. It doesn't matter that the parameter type is Locale but String is actually expected.

    After finding the potentially applicable methods, we go on to Identify Matching Arity Methods Applicable by Strict Invocation. This is where things go wrong. Remember how String::toUpperCase is an inexact method reference? That means it's not pertinent to applicability - the compiler doesn't consider the method reference at all during this step. See also this other question that also involves an inexact method reference expression causing overload resolution errors.

    So both funs are applicable by strict invocation. The next step is to find the most specific method. This step considers subtyping, like String is more specific than Object. But Comparator<String> is unrelated to Function<String, String>, so we cannot find the most specific method, and an error occurs.