Lambda Expressions

In Java 8, a lambda expression is a different way to write a method for a functional interface. Instead of using the book's examples, examples of using lambda expressions can be found here.

A lambda expression consists of a parameter list followed by the arrow token -> and a body of code:

(parameterList) -> { statements }

For example, the following lambda expression returns the sum of two ints:

(int x, int y) -> { return x + y; }

Note that this is like defining a method except that everything before the parameter list is omitted, and the arrow token is used. Depending on what the compiler can infer, the above lambda expression can be shortened to:

(x, y) -> x + y

Java does not allow arbitrary lambda expressions. Instead, the lambda expression must conform to a functional interface. For example, the above lambda expression is an IntBinaryOperator object.

Functional Interfaces

A functional interface is an interface that contains exactly one method (to be more precise, exactly one abstract method). Examples in this course include Comparator (the compare method) and ActionListener (the actionPerformed method).

Java 8 introduced several new interfaces for lambda expressions in the java.util.function package including:

The LambdaExamples class in lambda.zip contains examples of using these interfaces. Note that there are three (or more) ways of implementing equivalent code.

  1. Separate .java file: The functional interface can be implemented by a separate .java that codes the method of the interface. For example a class that allows strings to be sorted in reverse order, ignoring case, can be defined by:
    public class ReverseIgnoreCase implements Comparator<String> { 
        public boolean compare(String a, String b) {
                return b.compareToIgnoreCase(a);
        }
    }
    and used by:
    Comparator<String> reverseIgnoreCase = new ReverseIgnoreCase();
    Arrays.sort(stringArray, reverseIgnoreCase);
    Implementing a functional interface with a .java file is also illustrated by the Sigmoid, Even, Jumble, and Distance classes in lambda.zip.

  2. An anonymous inner class: An anonymous inner class is a way of constructing a new object from a new class written in the midst of other code. This is typically done to implement an interface. The above example could be written:
    Comparator<String> reverseIgnoreCase = 
        new Comparator<String>() {
            public boolean compare(String a, String b) {
                return b.compareToIgnoreCase(a);
        };
    Arrays.sort(stringArray, reverseIgnoreCase);
        

  3. A lambda expression: A lambda expression is more compact than an anonymous inner class. A functional interface only has one method to implement so the name of the method and other properties do not need to be specified. The above anonymous inner class could be rewritten as a lambda expression:
    Comparator<String> reverseIgnoreCase = 
        (String a, String b) -> {
            return b.compareToIgnoreCase(a);
        };
    Arrays.sort(stringArray, reverseIgnoreCase);
        

  4. Even shorter lambda expressions: The above example can be more compactly written as:
    Comparator<String> reverseIgnoreCase = (a, b) -> b.compareToIgnoreCase(a);
    Arrays.sort(stringArray, reverseIgnoreCase);
        
    or even:
    Arrays.sort(stringArray, (a, b) -> b.compareToIgnoreCase(a));
    
    Whether you can get away with leaving stuff out depends on what the compiler can infer from the context.

The doSigmoid, doEven, doJumble, and doDistance static methods in LambdaExamples contain more examples of the above techniques. They also illustrate some of the new functional interfaces in Java 8.

Lambda Expressions as Parameters

Because lambda expressions are objects, they can passed as parameters to methods. Some things to keep in mind are:

The Loop class in lambda.zip illustrates writing generic methods for lambda expression parameters.

The Loop.count method is intended to count the elements of a list (or any Iterable object) that satisfy some condition. The Predicate<T> functional interface allows the coding of a lambda expression from any reference type T to a boolean by calling its test method.

The Loop.filter method is intended to select a sublist of elements from a list that satisfy some condition. Again, the Predicate<T> functional interface is used.

The Loop.map method is intended to transform each value in a list to another value, possibly of a different type. The Function<T,R> functional interface allows the coding of a lambda expression from any reference type T to another reference type R by calling its apply method.

Note that Java can infer the types from the context of the method call: the functional interface type, the input parameter type, the return type, and the generic parameters.