Lambda Expressions

Goals

Concepts

Language

Library

Preview

final List<Point> points = getPoints();
points.sort(comparing(point -> point.getX()));

Lesson

You've already learned about the strategy pattern, in which a strategy class encapsulates the behavior required for performing some action. One of the archetypical strategy interfaces is the Comparator<T>, which can be passed to the List.sort(Comparator<? super E> comparator) method to specify how sorting should be performed. The Comparator<T> interface looks like this:
java.util.Comparator<T>
@FunctionalInterface
public interface Comparator<T> {

  int compare(T object1, T object2);

}

You'll notice that the Comparator<T> interface has only a single method: Comparator.compare(T object1, T object2). As Java does not allow references to individual methods, it is not unreasonable to imagine that the entire Comparator<T> interface was created just as a way to encapsulate this single compare(…) functionality. As such the the Comparator<T> interface, consisting of only one method, itself represents the function of comparing.

Functional Interfaces

We consequently refer to interfaces that contain a single unimplemented method as functional interfaces. As with other interfaces, functional interfaces require some implementation to perform a task. The purpose of a functional interface, however, is primarily to serve as a wrapper around some individual method—to large extent to work around Java's lack of the ability to reference some method implementation directly.

Recalling the Point class from previous lessons, you might make a PointPlotter strategy for placing the points on some two-dimensional plane. In fact you might even generalize the interface to simply Plottor<T> so that someone could specify a plotter for any type (including points, rectangles, and circles):

A general Plotter<T> strategy interface for plotting objects on a plane.
@FunctionalInterface
public interface Plotter<T> {

  void plot(T object);

}

To take advantage of the Plotter<T> strategy, we could create a Scene class that holds points and plots them. In this simplified example we ignore rectangles and other shapes, concentrating solely on points.

A scene that holds points and plots them.
public class Scene {

  private final Plotter<Point> plotter;

  private final Set<Point> points; //TODO implement methods for adding points

  /**
   * Plotter strategy constructor.
   * @param plotter The strategy for plotting points.
   */
  public Scene(@Nonnll final Plotter<Point> plotter) {
    this.plotter = checkNotNull(plotter);
  }

  /** Plots all the points on the plane. */
  public void plot() {
    for(final Point point : points) {
      plotter.plot(point);  //plot the point using the strategy
    }
  }

}

You could then create an implementation of Plotter<Point> named SystemOutPointPlotter, or you might decide just to use an inner class, as you only need one reusable plotter for printing to System.out.

Implementing a Plotter<Point> for printing to System.out.
public static final Plotter<Point> SYSTEM_OUT_POINT_PLOTTER = new Plotter<Point>() {
  @Override
  public void plot(final Point point) {
    System.out.println(point);
  }
};
final Scene scene = new Scene(SYSTEM_OUT_POINT_PLOTTER);
scene.plot();

Lambda Expressions

You probably noticed in the implementation of SYSTEM_OUT_POINT_PLOTTER above that most of the code is boilerplate—sections of code that appear virtually unchanged in all Plotter<T> implementations and do not contribute to the actual logic of the implementation. In fact the actual unique code is highlighted below The other parts of the implementation the compiler could have figured out on its own, because it was obvious we were assigning the inner class to a variable of type Plotter<Point>.

A Plotter<Point> for printing to System.out, with unique logic highlighted.
public static final Plotter<Point> SYSTEM_OUT_POINT_PLOTTER = new Plotter<Point>() {
  @Override
  public void plot(final Point point) {
    System.out.println(point);
  }
};

If we extract that logic out and separate the two parts with the arrow -> operator, we produce a lambda expression which is simply a compact representation of functionality that can be executed at a later time.

(final Point point) -> System.out.println(point)

You can use this lambda in any context that requires a Plotter<Point>, and the Java compiler will automatically infer the appropriate boilerplate code to wrap around the code in the lambda! A lambda can be used anywhere a functional interface is called for.

Using a lambda to implement a Plotter<Point> for printing to System.out.
public static final Plotter<Point> SYSTEM_OUT_POINT_PLOTTER = point -> System.out.println(point);
final Scene scene = new Scene(SYSTEM_OUT_POINT_PLOTTER);
scene.plot();

A lambda is so compact that you might even want to define a throwaway lambda inline at the point of use:

Declaring a lambda for Plotter<Point> at the point of use.
final Scene scene = new Scene(point -> System.out.println(point));
scene.plot();

A lambda can reference variables outside the lambda expression proper. The caveat is that the variable must be effectively final, which means that, even if it isn't declared final, there must be no way for it to change. Anonymous classes have the same restriction, except that they require an referenced variables to actually be declared final. It's still a good idea to mark all effectively final variables as final, to make it clear to other developers.

Here is an example of a lambda accessing a label string declared before the lambda expression:

Referencing a variable declared outside a lambda expression.
final String label = "point";
final Scene scene = new Scene(point -> String.format("%s: %s", label, point));
scene.plot();

Return Values

You can return a value from a lambda expression just as you would from any method, except that you dispense with the return statement. Assume we have a list of Point instances and we would like sort them based upon their X coordinates. From the lesson over the strategy pattern you already learned how to provide sorting using an anonymous class instance:

Sorting a list of points using an anonymous Comparator<Point> implementation.
final List<Point> points;  //TODO get points
points.sort(new Comparator<Point>() {
  @Override
  public int compare(@Nonnull final Point point1, @Nonnull final Point point2) {
    return Integer.compare(point1.getX(), point2.getX());
  }
});

A lambda expression can perform identical functionality using a much more compact syntax.

Sorting a list of points using a lambda expression.
final List<Point> points;  //TODO get points
points.sort((point1, point2) -> Integer.compare(point1.getX(), point2.getX()));

Functions

The term functional interface was chosen to denote those interfaces that effectively wrap around a single method and as such are similar to mathematical functions. Many appearances of the strategy pattern in fact look as if they are invoking some mathematical function:

If your use case is as general as some of these listed examples, Java has provided in the java.util.function package a long list of functional interfaces for you to use. Here are some of the most useful ones.

Consumer<T>
accept(T) takes a value and does something with it.
Function<T, R>
apply(T) takes an input value and returns an output value of type R. This is the quintessential functional interface.
Predicate<T>
test(T) takes some value, tests it, and returns the boolean result of the test.
Supplier<T>
get() returns an instance of type T.

Use these and other functional interfaces instead of making your own! For example if you wanted to remove only certain elements from a collection, you might be tempted to create a special functional interface for this:

Removing elements from a collection using a custom test strategy.
/** Filter for determining which elements to remove from a collection. */
@FunctionalInterface
public interface RemoveFilter<E> {
  /** Tests an element for removal.
   * @param element The element to test.
   * @return <true> if the item should be removed.
   */
  boolean shouldRemove(E element);
}
/**
 * Removes elements from a collection if they match a given filter.
 * @param collection The collection from which to remove items.
 * @param filter The strategy for determining whether an element should be removed.
 */
public static <E> removeFromCollection(@Nonnull final Collection<E> collection,
    @Nonnull final RemoveFilter<? super E> filter) {
  final Iterator<E> iterator = collection.iterator();
  while(iterator.hasNext()) {
    if(filter.shouldRemove(iterator.next())) {
      iterator.remove();
    }
  }
}
final List<String> strings = …;
//remove all strings that start with "foo" from the list
removeFromCollection(strings, string -> string.startsWith("foo"));

Upon closer examination, you might realize that your RemoveFilter<E> is no more than a general functional interface for testing some value—equivalent to a Java Predicate<T>. It would therefore be better to use the existing Predicate<T> functional interface instead of creating your own. Your lambda expression would not need to change.

Removing elements from a collection using a Predicate<T>.
/**
 * Removes elements from a collection if they match a given filter.
 * @param collection The collection from which to remove items.
 * @param filter The strategy for determining whether an element should be removed.
 */
public static <E> removeFromCollection(@Nonnull final Collection<E> collection,
    @Nonnull final Predicate<? super E> filter) {
  final Iterator<E> iterator = collection.iterator();
  while(iterator.hasNext()) {
    if(filter.test(iterator.next())) {
      iterator.remove();
    }
  }
}
final List<String> strings = …;
//remove all strings that start with "foo" from the list
removeFromCollection(strings, string -> string.startsWith("foo"));

In you don't even need to write the removeFromCollection(…) method, because Java already added a default method to the Collection<E> interface that uses a Predicate<T> to do the same thing: Collection.removeIf(Predicate<? super E> filter).

Removing elements from a collection using Collection.removeIf(…).
final List<String> strings = …;
//remove all strings that start with "foo" from the list
strings.removeIf(strings, string -> string.startsWith("foo"));

Higher-Order Functions

You no doubt noticed that functions make good use of indirection; they delegate the task of performing some logic and/or producing a value. As you learned early on, it's always possible (and sometimes useful) to add another layer of indirection. A prime example of this technique is a higher-order function, which is a function that either takes another function as a parameter or produces a function as a result.

You already know from earlier in this lesson that you can use a lambda expression to produce an instance of the Comparator<T>.compare(T, T) functional interface:

Using a lambda expression for a Comparator<T>.
final List<Point> points = …;
points.sort((point1, point2) -> Integer.compare(point1.getX(), point2.getX()));

The Comparator<T> interface has a static method Comparator.comparing(Function<? super T,? extends U> keyExtractor). Its complicated-looking signature indicates that it takes a Function<T, R> as a parameter and produces a Comparator<T>. In this case both the parameter and the return type are functional interfaces; either of these would have made Comparator.comparing(…) a higher order function.

But what does the Comparator.comparing(…) method do? The return type is simple enough: Comparator.comparing(…) must be something that creates a comparator for us. The parameter is a Function<T, R> for extracting a key (that is, some value to directly compare) from some other object. In terms of our Point class, the key would be the X coordinate—that is the value we're comparing the points on.

Thus if we pass a Function<Point, Integer> to Comparator.comparing(…) that takes a Point and returns Point.getX(), then Comparator.comparing(…) will return a Comparator<Point> that compares two points by their X coordinates. We can implement a Function<Point, Integer> using the lambda expression point -> point.getX():

Using Comparator.comparing(…) to produce a Comparator<T> for comparing Point instances.
final List<Point> points = …;
points.sort(Comparator.comparing(point -> point.getX()));

Internal Iteration

You've had quite a journey discovering various ways to iterate over a series of items. Your first stop was learning to increment an index until you reached the length of an array:

Iterating over an array by manually incrementing an index.
final String[] numbers = new String[]{"one", "two", "three"};
for(int i = 0; i < numbers.length; i++) {
  System.out.println(numbers[i]);
}

Manually manipulating an index is error-prone, not to mention tedious with all the accompanying boilerplate. You have already learned about the iterator pattern, which encapsulated the iteration functionality into a separate iterator instance that could be manipulated:

Iterating over a list using an iterator.
final List<String> numbers = Arrays.asList("one", "two", "three");
final Iterator<String> iterator = numbers.iterator();
while(iterator.hasNext()) {
  System.out.println(iterator.next());
}

Any instance of java.lang.Iterable<T> is able to provide a java.util.Iterator<T>. Still you must manually step through the sequence using Iterator.hasNext() and Iterator.next(). The use of the enhanced for loop helps this somewhat, by letting Java work with the Iterator<T> behind the scenes, but in the compiled code iteration nonetheless occurs by calling the iterator methods.

Iterating over a list using an enhanced for loop.
final List<String> numbers = Arrays.asList("one", "two", "three");
for(final String number : numbers) {
  System.out.println(iterator.next());
}

All of these approaches still require some logic, external to the collection being examined, that will manually step through the sequence; such approaches are referred to as external iteration.

Java also provides a way to iterate across the items in an Iterator<T> that completely turns things around: rather than your code calling the iterator to step through the sequence, the iterator will call your code! The method Iterable.forEach(Consumer<? super T> action) will call your implementation of the Consumer<T> strategy again and again—passing each item in the iterator, one at a time, to the Consumer.accept(T) method. This approach is called internal iteration because the Iterable<T> perform the iteration internally and calls back to your action strategy.

Without lambda expressions, it would be unwieldy to provide an implementation (especially an anonymous class implementation) of Consumer<T> to the Iterable.forEach(…) method. But the syntax becomes simple using a lambda:

Iterating over a list using internal iteration with a lambda expression.
final List<String> numbers = Arrays.asList("one", "two", "three");
numbers.forEach(number -> System.out.println(number));

What benefits does internal iteration bring, other than allowing the code to be even more compact that before? Importantly internal iteration completely removes any remaining chance that you might make a programming error when manually stepping through a sequence using external iteration. In future lessons you will also discover how internal iteration can bring about efficiencies by reordering the items; combining or discarding unnecessary operations; or even dividing up tasks among different processors as appropriate. In short, delegating the iteration to the collection itself relieves this responsibility from the developer, allowing the developer to concentrate on the actual functionality to perform.

Drawbacks

Lambda expressions are excellent tools for making your code less verbose and more readable—and the more your code can be understood by humans, the less humans are likely to make errors in modifying the code. Nevertheless this terseness brings some drawbacks as well.

Reusability

Creating bits of one-off logic on the fly can result in a lot of repeated code, because lambdas that aren't saved cannot be reused. One way to mitigate this would be to assign a lambda to some functional interface constant.

Exceptions

Many of Java's provided functional interfaces do not declare any exceptions, making it difficult for a lambda expression to report errors. This becomes even more tedious when lambdas are used with internal iteration, as the exceptions will be thrown by the internal iteration method (e.g. Iterable.forEach(…)) as it steps through the items internally. If you have a checked exception you need to throw, you might choose a function interface that declares checked exceptions, or write your own. Another approach would have your lambda catch any checked exceptions and rethrow them as unchecked exceptions.

Review

Summary

Common variations of lambda expressions:

In the Real World

Think About It

Self Evaluation

Task

Implement a presentation layer method in the Booker class specifically for presenting a list of books to the user.

See Also

References

Resources

Acknowledgments