0

I'm learning about lambda expressions. I don't understand how a comparator is returned from method reference.

I want to sort a list of persons by age.

To do that, I have a method to find the age difference:

public int ageDifference(final Person other) {
    return age - other.age;
}

The sorted method needs as parameter a Comparator

Stream<T> sorted(Comparator<? super T> comparator);

My lambda expression is:

people.stream()
.sorted(Person::ageDifference)
.collect(toList());

How Person::ageDifference is trasformed in a Comparator<Person>?

My complete example:

public class Person {

private final String name;
private final int age;

public Person(final String theName, final int theAge) {
    name = theName;
    age = theAge;
}

public String getName() {
    return name;
}

public int getAge() {
    return age;
}

public int ageDifference(final Person other) {
    return age - other.age;
}

public String toString() {
    return String.format("%s - %d", name, age);
}

public static void main (String args[] ){
    final List<Person> people = Arrays.asList(
            new Person("John", 10),
            new Person("Greg", 30),
            new Person("Sara", 20),
            new Person("Jane", 15));

    List<Person> ascendingAge =
            people.stream()
                    .sorted(Person::ageDifference)
                    .collect(toList());

    System.out.println(ascendingAge);
}
}

Output: [John - 10, Jane - 15, Sara - 20, Greg - 30]

Jesus Zavarce
  • 1,729
  • 1
  • 17
  • 27
  • 4
    https://stackoverflow.com/questions/17913409/what-is-a-sam-type-in-java – leonardkraemer Nov 20 '18 at 18:26
  • 1
    Right, `Comparator` is a SAM type. A Single Abstract Method. So the compiler "just knows" to substitute the `Comparator` type and have it call the method you supply, `Person::ageDifference`. The link Leonard Kraemer supplied explains in more detail. – markspace Nov 20 '18 at 18:34
  • 1
    As a lambda it'd look like: `(p1, p2) -> p1.ageDifference(p2)` – Slaw Nov 20 '18 at 18:43

1 Answers1

5

I guess your main confusion is this:

Comparator<T> represents a method that takes two parameters of type T and returns a int. My ageDifference method accepts only one Person parameter. How can that become a Comparator<Person>?

Note that ageDifference is an instance method. To call it, not only do you need the parameters, you also need an instance of Person. In this case, you need 2 Persons to call the method - one on which you call ageDifference, and the other one you pass as a parameter:

me.ageDifference(someoneElse)
^                      ^
|                      |
        Two people!

Isn't this just like a static method that accepts two parameters?

Therefore, Java is smart enough to know that you need two people to call Person::ageDifference, so the method reference is treated as having two parameters.

In general, an instance method of a class T accepting parameters P1, P2 ... Pn and returning type R can be treated as a static method accepting parameters T, P1, P2 ... Pn and returning R.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • to complete your good answer, the reason why the code above compiles is in the way of how the new lambda functionality works in Java 8. It relies on a concept which is informally known as "single abstract method (SAM)" interfaces. The basic idea is that any interface with one abstract method can be automatically implemented by any lambda or method reference, if the lambda or method reference matches the SAM in the interface. – Jesus Zavarce Dec 13 '18 at 08:08