Ambiguity in method references
Asked Answered
G

1

12

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?

Godson answered 31/5 at 7:31 Comment(0)
J
16

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.

Joacima answered 31/5 at 8:6 Comment(5)
regarding the statment "So both funs are applicable by strict invocation", did you mean they are inapplicable instead? As String::toUpperCase is indeed an inexact method reference expression.Godson
@RohanJaiswal No, as far as that particular step is concerned, they are both applicable, because the method reference expression is not pertinent to applicability. i.e. it doesn't take part in determining whether an overload is applicable or not. Stricter checks are applied later, but the compiler already ran into an error (namely, there is no most specific method) before that point.Joacima
So to rephrase, toUpperCase is not found to be applicable by strict_invocation phase, and it moves on to the next phases, i.e., Identify Matching Arity Methods Applicable by Loose Invocation, etc and then finally to Choosing the Most Specific Method at which the compiler throws an error.Godson
@RohanJaiswal Just to be on the same page here, we are not determining the applicability of toUpperCase. We are determining which overloads of fun is applicable. And in the strict invocation phase, both are found to be applicable. Quote: "If m is not a generic method, then m is applicable by strict invocation if, for 1 ≤ i ≤ n, either ei is compatible in a strict invocation context with Fi (§5.3) or ei is not pertinent to applicability." The method reference is not pertinent to applicability, so both funs are applicable by strict invocation, regardless of other factors.Joacima
@RohanJaiswal Note that there is no need to consider whether "ei is compatible in a strict invocation context with Fi", since the latter disjunct "ei is not pertinent to applicability" is always true. Since both methods are applicable by strict invocation, the loose & variable-arity steps are skipped. And yes, the error occurs at the "choosing the most specific method" step.Joacima

© 2022 - 2024 — McMap. All rights reserved.