Java Programming/Lambda expressions

Introduction edit

Lambda expressions were introduced in Java 8 as a concise way to describe a piece of functionality as a first class member of the language. While the principle is similar to the use of anonymous inner classes to satisfy interfaces, lambda expressions make the code much more legible.

I.e., instead of

Collection<Book> results = library.findAll(new Predicate() {
   @Override
   public boolean test(Book book) {
       return book.getTitle().contains("Java");
   }
});

we can say simply:

Collection<Book> results = library.findAll(book -> book.getTitle().contains("Java"));

Syntax edit

A lambda expression consists of two parts separated by the -> characters, referred to as an "arrow":

Parameters -> Body

Parameters edit

The Parameters define the inputs to the expression. Generally, the Parameters are a comma-separated list of types and identifiers surrounded by parentheses:

( Type1 identifier1 , Type2 identifier2 ) -> Body

However, the types are often inferable from the context and can be omitted:

( identifier1, identifier2 ) -> Body

And also, if there is exactly one input and its type is inferable, the parentheses can be omitted:

identifier1 -> Body

Body edit

The body of the lambda expression describes what should be done with those inputs (if any). In general, the body is a block like any other:

Parameters -> {
    statements;
    ...
    return statement;
}

The return statement can be omitted for expressions that simply operate on the inputs.

Parameters -> {
    statements;
    ...
}

If the body consists only of a single statement, the braces and terminal semi-colon can be omitted:

Parameters -> statement

If the expression consists of a single return statement, the braces, return keyword, and terminal semi-colon can all be omitted:

Parameters -> result

Terminology edit

The java.util.function namespace contains many structures out-of-the-box, introducing some standard terminology falling under 3 broad types:

  1. "Functions": real functions in the mathematical sense, taking exactly one input and providing exactly one output;
  2. "Consumers": entities that accept inputs but do not provide any output (in the conventional sense); and
  3. "Suppliers": entities that provide a value every time it is invoked without requiring any input.
Interface Inputs Result Signature
Function<T, R> T R T -> R Accepts one input of type T and returns something of type R
Consumer<T> T T -> Accepts one input of type T and does not return anything (void)
Supplier<R> R -> R Does not accept any inputs and returns objects of type R

Function and Consumer also have "Bi" variants accepting two inputs:

Interface Inputs Result Signature
BiFunction<S, T, R> S, T R (S, T) -> R Accepts two inputs, one of type S and another of type T, and returns something of type R
BiConsumer<S, T> S, T (S, T) -> Accepts two inputs, one of type S and another of type T and does not return anything (void)

Further Reading edit

  • Streams/Collections
  • Functional Interfaces
  • Method references