Instance Method Reference and Lambda Parameters

I am having trouble understanding the syntax for a method reference, where there are two parameters a and b, and the reference is to a method of a on b.

For example I understand how

Arrays.sort(personArray, comparators::compareByName);

is equivalent to

Arrays.sort(personArray, (o1, o2) -> comparators.compareByName(o1, o2));

because in that case the lambda parameters match the method call parameters (o1, o2).

Howevever for this lambda

stream.sorted((o1, o2) -> o1.compareToIgnoreCase(o2));

my IDE tells me that is equivalent to:

stream.sorted(String::compareToIgnoreCase);

and I am not finding a rule for replacing that syntax: a.method(b) with a method reference.

For example, what if there are three or more parameters to the lambda? Is that legal? Does the first parameter become the method target, and the remaining become the parameters?


Solution 1:

I think you're looking for JLS section 15.13.3, which includes:

If the form is ReferenceType :: [TypeArguments] Identifier, the body of the invocation method similarly has the effect of a method invocation expression for a compile-time declaration which is the compile-time declaration of the method reference expression. Run-time evaluation of the method invocation expression is as specified in §15.12.4.3, §15.12.4.4, and §15.12.4.5, where:

  • The invocation mode is derived from the compile-time declaration as specified in §15.12.3.

  • If the compile-time declaration is an instance method, then the target reference is the first formal parameter of the invocation method. Otherwise, there is no target reference.

  • If the compile-time declaration is an instance method, then the arguments to the method invocation expression (if any) are the second and subsequent formal parameters of the invocation method. Otherwise, the arguments to the method invocation expression are the formal parameters of the invocation method.

Note the last two bullets, basically.

For example, what if there are three or more parameters to the lambda? Is that legal? Does the first parameter become the method target, and the remaining become the parameters?

Yup :)

Solution 2:

I would give a couple of examples here, for those who find Oracle documentation a bit hard to take in. Imagine you need a reference to a Comparator instance:

.sorted(String::compareTo)

String::compareTo is identical to:

(String a, String b) -> a.compareTo(b);

Because, as Jon explained, a method reference will be transformed to a lambda that will expect 2 parameters. The actual arbitrary object passed in the stream as a first argument, and one more parameter(since Comparator expects int compare(T o1, T o2)). Another case:

.map(Employee::getSalary)

In this case map expects: Function. Function requires implementation of R apply(T var1) - a method with 1 argument. In this case the only parameter that will be passed to the lambda is the actual arbitrary object - instance on Employee.

To sum up - depending on the compile time context, method reference to arbitrary object will always be "transformed" into a lambda that expects that object as a first parameter + any number of parameters that the target method requires in the same corresponding order.