I have seen that in Java 8, one can define a comparator like this:
Comparator c = (Computer c1, Computer c2) -> c1.getAge().compareTo(c2.getAge());
which is equivalent to:
Comparator d = new Comparator<Computer> () {
@Override
public int compare(Computer c1, Computer c2){
return c1.getAge().compareTo(c2.getAge());
}
};
I'd like to understand how this works. In the second example, it is fairly simple: A Comparator
object is created with a method compare
which performs the comparison by using the compareTo
method in the age
property of Computer
. This method is simply called by us when we do:
Computer comp1 = new Computer(10);
Computer comp2 = new Computer(11);
d.compare(comp1, comp2); // -1
But what's going on in the first example, when using a lambda? It looks to me like we are setting the Comparator
to be equal to a method that performs comparison. But this cannot be, because a Comparator
object is an object that has a method compare
. I've learned that lambdas can be used with functional interfaces (interfaces with only one method). But Comparator
is not a functional interface (it has many other methods other than compare
!). So how does the Java interpreter know that it is the compare
method we are implementing?
Explanation
Comparator
is a functional interface (only demands one method). Thus, you can create instances of it by using a lambda expression.
It behaves very similar to other methods of creating instances, such as a regular class which extends or an anonymous class.
The lambda refers to the one method a functional interface demands. Since there is only one method, it is not ambiguous. The lambda names the input arguments and then gives an implementation for the method (it provides a body).
Overview
You have the following options to create instances of interfaces or abstract classes:
- Create a class that extends and use new
- Use an anonymous class
Supposed we have an interface which only offers one method (it's called functional interface then), we additionally have the following two options to create instances of it:
- Use a lambda expression
- Use a method reference
As example, we want to create a multiplication instance, using the following interface:
@FunctionalInterface
public interface Operation {
int op(int a, int b);
}
Create a class that extends and use new:
public class Multiplicator implements Operation { @Override public int op(int a, int b) { return a * b; } } // Usage Operation operation = new Multiplicator(); System.out.println(operation.op(5, 2)); // 10
Use an anonymous class:
Operation operation = new Operation() { @Override public int op(int a, int b) { return a * b; } }; // Usage System.out.println(operation.op(5, 2)); // 10
Use a lambda expression:
Operation operation = (a, b) -> a * b; System.out.println(operation.op(5, 2)); // 10
Use a method reference:
// Somewhere else in our project, in the `MathUtil` class public static int multiply(int a, int b) { return a * b; } // Usage Operation operation = MathUtil::multiply; System.out.println(operation.op(5, 2)); // 10