Why can a lambda expression be used as a comparator?

The OCP Study Guide provides an example of using a comparator that can be initialized in two ways. The first is through the anonymous class:

Comparator<Duck> byWeight = new Comparator<Duck>(){
    public int compare(Duck d1, Duck d2){
        return d1.getWeight() - d2.getWeight();
    }
};

      

I can understand that. According to this book, this can be replaced with a lambda expression like this:

Comparator<Duck> byWeight = (d1,d2) -> d1.getWeight() - d2.getWeight();

      

Now I don't understand this. The lambda expression doesn't return a Comparator object, which it couldn't now when I think about it, since Comparator is an interface.

So the operator new

in the first example refers to the anonymous class that is being instantiated called Comparator, because this anonymous class implements the Comparator interface?

What exactly happens in example 2? Is the object somehow created from a lambda expression? In this example, are you using byWeight

right as a reference variable?

I don't really understand this, can anyone explain it? Thank.

+3


source to share


9 replies


If you've read the interfaceComparator

documentation , you can read:

Functional interface:     This is a functional interface and therefore can be used as a target for a lambda expression or method reference.

So the interface is Comparator<T>

implemented as :

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2);

    // ...

}
      

Now, if we look at the documentation @FunctionalInterface

, we see:

An informative annotation type used to indicate that an interface type declaration is intended for a functional interface , as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method . Since the default methods are not abstract. If an interface declares an abstract method that overrides one of the public methods java.lang.Object

, that also ignores the interface of the abstract method, since any implementation of the interface will have an implementation from java.lang.Object

or elsewhere.



Basically, if you have an interface with an abstract method one , and you annotate an interface like @FunctionalInterface

, this interface is the aim for functions that you more or less create an anonymous class that implements the functional interface and you specified function is an implementation the only abstract method.

In other words, the expression:

Comparator<Duck> byWeight = <somelambda>

      

equivalent to:

Comparator<Duck> byWeight = new Comparator<Duck>(){
    public int compare(Duck d1, Duck d2){
        return <somelambda>(d1,d2);
    }
}

      

+9


source


In the first block of code, the instantiated object implements Comparator<Duck>

, but the corresponding class has no name (is anonymous).

In the second block of code, the same thing happens. Since an interface Comparator

only defines one method (named compare

), you can abbreviate the creation of an (anonymous) interface implementation with a lambda expression.



The variable byWeight

can be used in 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

called in this implementation, the definition provided using the lambda expression is used.

+3


source


Java 8 Comparator<T>

adds annotation with @FunctionalInterface

. The documentation says:

An informative annotation type used to indicate that an interface type declaration should be a functional interface as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method. Since the default methods are not abstract. If an interface declares an abstract method that overrides one of the public methods java.lang.Object, that also ignores the interface of the abstract method, since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.

Note that examples of functional interfaces can be created using lambda expressions, method references, or constructor references.

If a type is annotated with this annotation type, compilers are required to generate an error message if:

A type is an interface type, not an annotation type, enumeration, or class. An annotated type satisfies the requirements of a functional interface. However, the compiler will handle any interface that defines a functional interface as a functional interface, regardless of whether the FunctionalInterface annotation is present in 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.

+2


source


An interface Comparator

is a Functional interface , which means that this interface can only contain one abstract method.

Then you can use lambda expression to define the implementation of this abstract method, basically (d1,d2) -> d1.getWeight() - d2.getWeight();

it is the implementation of the abstract method int compare(T o1, T o2);

.

As a functional interface contains only one abstract method, you can use a lambda expression to define the implementation of such an interface

+2


source


A comparator is just a function that takes two parameters and returns an int.

Effectively, what's going on here is that the compiler is adept at deducing what should be the right-hand side 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 it Comparator<T>

is defined as a functional interface:

It is a functional interface and therefore can be used as a destination for a lambda expression or method reference.

+1


source


First of all, you create a new anonymous class (which has a method with behavior).

In the second case, you just expose the behavior (think of this as a way to share a function, method, without seeing the surrounding class, even if it is created transparently).

I remember it was clearly explained in Java Tutorial - Lambda Expressions

One problem with anonymous classes is that if the anonymous class is very simple, such as an interface containing only one method, then the syntax of anonymous classes can seem cumbersome and obscure. In these cases, you usually try to pass the functionality as an argument to another method, such as what action to take when someone clicks a button. Lambda expressions allow you to do this, treat functionality as a method argument, or code as data.

I suggest focusing on the fact that using lambdas you are trying to simply expose the behavior of some class or component, your example might be in Collections.sort

.

lambdas gives you a cleaner, simpler expression by avoiding the pattern of anonymous class declarations.

0


source


In general, lambdas behave very much like inner classes, but allow for stronger syntax at a lower cost.

Since Java8 Comparator

is @FunctionalInterface

( JavaDoc ) so it can be used with lambda expressions. That is, you can define a Bi-Function (a function with two arguments) with a return type int

and use it like Comparator

in the second example. Both instances Comparator

can be used in exactly the same way in your remaining code.

0


source


Lambda expression is just shorthand for functional interface (single function interface), you don't need to write new function name / name just write parameter list in (

yourParameterListHere )

and then ->

and after that write what to do / return (i.e. body functions). you can also write it down with {

}

like

Comparator<Duck> byWeight = (d1,d2) -> { d1.getWeight() - d2.getWeight(); }

      

0


source


You can think of a lambda as a method that doesn't belong to a class, that can be passed as part of the data.

Or, with very little mental shift, you can think of it as an object with only one method.

There is a set of interfaces designated as functional interfaces . This is referred to in the Javadoc as:

Functional interface:

It is a functional interface and therefore can be used as a target for a lambda expression or method reference.

This is a technical way of saying that since they only have one method, and they are marked functional , the compiler can treat the lambda as an object with that interface. He knows how to use a lambda as an implementation of a single method in an interface.

So you can do:

Comparator<Pet> byNameComparator = (f1,f2) -> f1.name().compareTo(f2.name());
Predicate<Customer> isVip = cust -> cust.orderCount > 20;
Callable<Item> getResult = () -> queue.getItem();
Function<Integer,Integer> multiply = (a,b) -> a * b;

      

... etc.

And wherever the parameter type is a functional interface, you can use the lambda directly, so given:

 public void sort(Comparator cmp);

      

... you can call it like:

 foo.sort( (a,b) -> a.name().compareTo(b.name()));

      

0


source







All Articles