Tino APCS

Lambda Expressions

The Problem

In the previous Lesson, we reduced our code to the following:

Button btn = new Button();        
btn.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        System.out.println("Hello World!");
    }
});

While this is relatively short, it still contains a lot of redundant information. The compiler already knows what kind of parameter it’s looking for, so we could theoretically get rid of the class name. Additionally, we are only overriding one method, so the compiler theoretically doesn’t need to know the name of the method we are overriding because there is only one choice.

To address this issue, Java 8 added lambda expressions.

Lambda Expressions

With a lambda expression, we can reduce our code to the following:

Button btn = new Button();        
btn.setOnAction( (ActionEvent event) -> { System.out.println("Hello World!"); } );

We have effectively created an unnamed EventHandler and overridden its unnamed handle(ActionEvent event) method. A lambda expression is essentially an unnamed method replacement for a class that has only one method.

Syntax The general syntax of a lambda expression is:

(param1,param2,...) -> {
    statement1;
    statement2;
    return objectOfTheRightType;
}

If the method being called has only one parameter, you can omit the ( ):

param1 -> {
    statement1;
    statement2;
    return objectOfTheRightType;
}

If the method being called has only one statement, you can omit the { }:

param1 -> statement1;

This leaves our code in the following form:

Button btn = new Button();
btn.setOnAction(event -> System.out.println("Hello World!"));

Which is often just written as:

btn.setOnAction(e -> System.out.println("Hello World!"));

When to Use Lambda Expressions

A functional interface is an interface with exactly one abstract method.

If a class implements one or more functional interfaces, you can use a lambda expression to create an instance of the class and override the single method in question. Java will always be able to figure out which method you mean by its signature.

  • Lambda expressions can be used to instantiate any functional interface (any interface with only one abstract method)
  • This includes every class in the java.util.function package, as well as a couple other notable classes
  • The Comparator class was retrofitted from 7 to 8 with a default method to make it functional, as shown in the next example

Sorting

Let’s say you wanted to sort a list, named list, of strings by length then by lexicography. The appropriate API method is

Collections.sort(List<T> , Comparator<? super T>)

You could use an anonymous class:

Collections.sort(list,new Comparator<String>(){
@Override
    public int compare(String s1, String s1) {
    if(s1.length() != s2.length())
                    return s1.length() - s2.length();
                else
                    return s1.compareTo(s2);
               });

However, a lambda expression will simplify the syntax.

An equivalent lambda expression is:

Collections.sort(list,new Comparator<String>(){Collections.sort(list,(
String s1, String s2)->
{
    if(s1.length() != s2.length())
        return s1.length() - s2.length();
    else
        return s1.compareTo(s2);
}

Here's the same statement in compact form:

Collections.sort(list, (s1, s2) ->  
        s1.length() != s2.length()) ? s1.length() - s2.length() : s1.compareTo(s2));

Double colon Notation

  • If you are calling a method that already exists rather than overriding a method, you can use "double colon notation" to be even more brief.

Ex. 1 The Stream.filter(Predicate) method takes the elements of a stream and filters out elements that don’t match the predicate’s method (a Predicate takes in one Object parameter and returns a boolean). Let’s say we wanted to filter out all the null elements.

The appropriate method is Object.isNotNull(Object), so we write:

stream.filter(e -> Object.isNotNull(e));

However, the compiler already knows it’s looking for a method that takes an object and returns a boolean, as isNotNull(Object) already does. This allows us to use functional notation and just let the compiler fill in the rest:

stream.filter(Object::isNotNull);

Ex. 2 The :: operator can also be used to reference a constructor. The method ollectors.toCollection(Supplier<? extends Collection> method requires a Supplier, which takes no parameters and returns a collection.

Using a lambda expression:

Collectors.toCollection(() -> new ArrayList());

Using functional notation:

Collectors.toCollection(ArrayList::new);

Dark Mode

Outline