Why can a lambda expression be used as a Comparator?
If you read the documentation of the Comparator
interface, you can read:
Functional Interface: This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.
The Comparator<T>
interface is thus implemented like:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
// ...
}
Now if we look at the documentation of @FunctionalInterface
we see:
An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of
java.lang.Object
, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation fromjava.lang.Object
or elsewhere.
So basically if you have an interface with one abstract method, and you annotate the interface as a @FunctionalInterface
, then that interface is a target for functions: in that you more or less construct an anonymous class that implements the functional interface and the function you specify is the implementation of the only abstract method.
In other words, the expression:
Comparator<Duck> byWeight = <somelambda>
is equivalent to:
Comparator<Duck> byWeight = new Comparator<Duck>(){
public int compare(Duck d1, Duck d2){
return <somelambda>(d1,d2);
}
}
A comparator is basically just a function which takes two parameters and returns an int.
Effectively what's happening here is that the compiler is able to cleverly infer what the right-hand side needs to be because of how you declared the left-hand side.
Comparator<Duck> byWeight = (d1,d2) -> d1.getWeight() - d2.getWeight();
//^ ^ I know a Comparator<Duck> takes two Ducks.
// ^ I know a Comparator<Duck> returns an int
This is all possible because a Comparator<T>
is defined as a functional interface:
This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.
In the first code block the created object instance implements Comparator<Duck>
, but the corresponding class has no name (is anonymous).
In the second code block, the same thing happens. Because the Comparator
interface only defines a single method (named compare
), it is possible to abbreviate the creation of an (anonymous) implementation of the interface using a lambda expression.
The variable byWeight
can be used the same way in both examples. Everywhere a Comparator<Duck>
is required, byWeight
can be used - which corresponds to the type information of the variable. Internally, whenever compare
is invoked on this implementation, the definition provided using the lambda expression is used.
In Java 8 Comparator<T>
is annotated with @FunctionalInterface
. It's documentation says:
An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.
If a type is annotated with this annotation type, compilers are required to generate an error message unless:
The type is an interface type and not an annotation type, enum, or class. The annotated type satisfies the requirements of a functional interface. However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.
The most important part here is that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.
, which answers your question.