I read the Java 8 tutorial on Lambda Expressions and do not quite understand the Method Reference example for "Reference to an instance method of an arbitrary object of a particular type"
In the same tutorial there is an example "Reference to an Instance Method of a Particular Object" which look likes.
public int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
I can see this working because the method compareByName has the same signature as Comparator.compare, lambda (a, b) -> myComparisonProvider.compareByName(a, b) takes two arguments and calls a method with the same two arguments.
Now the "Reference to an instance method of an arbitrary object of a particular type" example uses String::compareToIgnoreCase
String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
The signature for that method is int compareTo(String anotherString)
and is different than Comparator.compare. The tutorial is not very clear but seem to imply you end up with a lambda such as (a, b) -> a.compareToIgnoreCase(b) I dont understand how the compiler decides what is acceptable for the second param of Arrays.sort I thought maybe it is smart enough to understand how to call that method, so I created an example.
public class LambdaTest {
public static void main(String... args) {
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase); // This works
// using Static methods
Arrays.sort(stringArray, FakeString::compare); // This compiles
Arrays.sort(stringArray, FakeString::compareToIgnoreCase); // This does not
// using Instance methods
LambdaTest lt = new LambdaTest();
FakeString2 fs2 = lt.new FakeString2();
Arrays.sort(stringArray, fs2::compare); // This compiles
Arrays.sort(stringArray, fs2::compareToIgnoreCase); // This does not
for(String name : stringArray){
System.out.println(name);
}
}
static class FakeString {
public static int compareToIgnoreCase(String a) {
return 0;
}
public static int compare(String a, String b) {
return String.CASE_INSENSITIVE_ORDER.compare(a, b);
}
}
class FakeString2 implements Comparator<String> {
public int compareToIgnoreCase(String a) {
return 0;
}
@Override
public int compare(String a, String b) {
return String.CASE_INSENSITIVE_ORDER.compare(a, b);
}
}
}
Can some one explain why the above two Arrays.sort don't compile even though they are using methods that are the same as String.compareToIgnoreCase method
A method reference is similar to lambda expression used to refer a method without invoking it while constructor reference used to refer to the constructor without instantiating the named class. A method reference requires a target type similar to lambda expressions.
Lambda expression is an anonymous method (method without a name) that has used to provide the inline implementation of a method defined by the functional interface while a method reference is similar to a lambda expression that refers a method without executing it.
Method references are a special type of lambda expressions. They're often used to create simple lambda expressions by referencing existing methods. There are four kinds of method references: Static methods. Instance methods of particular objects.
This is the difference between a method reference on some object and a method reference on the object being processed.
First the Oracle examples
Lets look at this first case:
public int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
Here, the method compareByName
is called on the passed in instance of myComparisonProvider
with each pair of arguments in the sort
algorithm.
So here, when comparing a
and b
we actually call:
final int c = myComparisonProvider.compareByName(a,b);
Now, in the second case:
String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
You are sorting a String[]
so the method compareToIgnoreCase
is called on the String
instance currently being sorted with the other String
as an argument.
So here, when comparing a
and b
we actually call:
final int c = a.compareToIgnoreCase(b);
So these are two different cases:
Onto your examples
Now in your first example, you also have a String[]
and you try and sort it. So:
Arrays.sort(stringArray, FakeString::compare);
So here, when comparing a
and b
we actually call:
final int c = FakeString.compare(a, b);
The only difference is compare
is static
.
Arrays.sort(stringArray, FakeString::compareToIgnoreCase);
Now, the String[]
is not a FakeString[]
so we cannot call this method on String
. Therefore we must be calling a static
method on FakeString
. But we cannot do this either, because we require a method (String, String) -> int
but we only have (String) -> int
- compilation error.
In the second example the problem is exactly the same, as you still have a String[]
. And compareToIgnoreCase
has the wrong signature.
The point you are missing is that in the String::compareToIgnoreCase
example; the method is called on the String
currently being processed.
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