Java’s functional interfaces

We previously stated that “when we pass a lambda function as a parameter to a sorting function, Java will automatically construct an object that implements Comparator and whose member function compare performs the steps defined by our lambda function.”. This behaviour arises from the fact that Comparator is a so-called “functional interface”, and Java treats lambda functions and function references as syntactic shortcuts to create objects that implement a functional interface. We could think of lambda functions and function references as simplified ways to express an anonymous class definition.

A functional interface in Java simply means an interface that has exactly one abstract member function. For example both of the next two example interfaces are functional interfaces because they have exactly one abstract function.

public interface SayHello {
  public void sayHello();     // An abstract member function.
}

public interface Comparator<T> {
  int compare(T a, T b);      // An abstract member function.
  boolean equals(Object obj); // Not abstract because an implementation is inherited from Object!
}

When a lambda function or a function reference appears in a context where an object that implements a certain funtional interface is expected, a Java compiler will automatically create an object that implements that interface. The implementation of the single abstract function required by the functional interface is based on the lambda function or the referenced function. From here on we will use the term “function object” to refer to an object that implements a functional interface.

Below is an example function helloTest that takes as a parameter an object sh of type SayHello. Because SayHello is a functional interface, we may provide the parameter sh using a lambda function or a function reference. It is of course possible to create the passed object also as from a separately defined class or by using an anonymous class definition. The example below uses each of the mentioned four ways to pass a SayHello object to the function helloTest.

public class FunctionalExample {
  public static interface SayHello {
    public void sayHello();
  }

  public static void helloTest(SayHello sh) {
    sh.sayHello();
  }

  public static class MyHello implements SayHello {
    @Override
    public void sayHello() {
      System.out.println("Hello from class MyHello!");
    }
  }

  public static void myHelloFunction() {
    System.out.println("Hello from a referenced function!");
  }

  public static void main(String[] args) {
    // Option #1: define the parameter to helloTest as an object of class MyHello.
    helloTest(new MyHello());

    // Option #2: define the parameter to helloTest using an anonymous class definitoon.
    helloTest(new SayHello() {
      @Override
      public void sayHello() {
        System.out.println("Hello from an anonymous class!");
      }
    });

    // Option #3: define the parameter to helloTest by using a lambda function.
    helloTest(() -> System.out.println("Hello from a lambda-function!"));

    // Option #4: define the parameter to helloTest by using a function reference.
    helloTest(FunctionalExample::myHelloFunction);
  }
}

The preceding code outputs:

Hello from class MyHello!
Hello from an anonymous class!
Hello from a lambda-function!
Hello from a referenced function!

When a Java compiler processes the part helloTest(() -> System.out.println("Hello from lambda-function!")) it can check that the function helloTest takes a function object of type SayHello as a parameter. The compiler can furthermore check that the interface SayHello has exactly one abstract member function and is thus a functional interface. This directs the compiler to automatically generate a similar object as if we had used an anonymous class definition that implements SayHello in such manner that the function sayHello performs the same steps as the lambda function. The part helloTest(FunctionalExample::myHelloFunction) would be processed otherwise in similar manner, but now the function sayHello would be based on the referenced function myHelloFunction.

Just as in the case of anonymous class definitions, we may store a reference to an object created from a lambda function or a function reference. The next example is similar to a previous example about anonymous class definitions, but now a lambda function is used instead.

Point2D[] ps = {new Point2D(2.5, -1.5), new Point2D(-3.5, 4.5), new Point2D(0.5, 0.5)};
Comparator<Point2D> comparator = (Point2D a, Point2D b) -> {          // Lambda function.
    int cmp = Double.compare(a.getX(), b.getX());
    if(cmp == 0) {
      cmp = Double.compare(a.getY(), b.getY());
    }
    return cmp;
  };
Arrays.sort(ps, comparator);  // Give the comparator object "comparator" as a parameter.
for(Point2D p : ps) {
  System.out.format(" (%.1f, %.1f)", p.getX(), p.getY());
}

Like discussed, we may use a lambda function or a function reference to automatically create a function object that implements an interface with exactly one abstract function. Here we naturally need to define the lambda function in such manner that it is compatible with the abstract function (the function parameters and the return type). Note that a lambda function or a function reference does not in itself express what interface an object created from it will implement: the compiler will infer the interface from the context, which usually means the type of the variable (including a function parameter) that will eventually refer to the created object.

Java class library contains also many other functional interfaces in addition to the already familiar Comparator interface. Some of them are introduced below. Their definitions are presented in concise manner: only the abstract member function declaration is shown. The interfaces may in addition contain e.g. default functions.

// The interface Predicate defines a "test", that is, a function that
// takes a parameter and returns a truth value true / false based on it.
public interface Predicate<T> {
  boolean test(T t);  // The function "test" checks if the parameter t fulfills some condition?
}

// The interface Function defines a function that takes
// one parameter of type T and returns a result of type R.
public interface Function<T,R> {
  R apply(T t);  // The function "apply" implements the function logic.
}

// The interface Unaryperator defines a function that takes
// one parameter of type T and returns a result of the same type T.
// UnaryOperator inherits the interface Function and the abstract
// function "apply" is actually declared by the interface Function.
public interface UnaryOperator<T> extends Function<T,T> {
  T apply(T t);  // The function "apply" implements the function logic.
}

// The interface BiFunction defines a function that takes two
// parameters of types T and U and returns a result of type R.
public interface BiFunction<T,U,R> {
  R apply(T t, U u);  // The function "apply" implements the function logic.
}

// The interface Binaryperator defines a function that takes two
// parameters of type T and returns a result of the same type T.
// BinaryOperator inherits the interface BiFunction and the abstract
// function "apply" is actually declared by the interface BiFunction.
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
  T apply(T t, T u);  // The function "apply" implements the function logic.
}

// The interface Consumer defines a function that takes one parameter
// of type T and returns no result (in some sense "consumes" the parameter).
public interface Consumer<T>
  void accept(T t);  // The function "accept" implements the function logic.
}

// The interface BiConsumer defines a function that takes two parameters of types
// T and U and returns no result (in some sense "consumes" the parameters).
public interface BiConsumer<T>
  void accept(T t, U u);  // The function "accept" implements the function logic.
}

// The interface Supplier defines a function that takes no parameters
// and returns a result of type R (in some sense "supplies" a new value).
public interface Supplier<R>
  R get();  // The function "get" implements the function logic.
}

When you implement function that takes some kind of a function object as a parameter, it is often feasible to use one of Java’s readily available functional interfaces to define the parameter’s type (if the parameters and return type of the interface’s abstract member function are suitable).

Below is en example function filter that takes a list container list and a function object implementing the interface Predicate as parameters, and returns an ArrayList that contains each item t of list for which the function call predicate.test(t) returns true. A technical detail is that the wildcard of the parameter predicate allows to use a Predicate object that can test an object of type T or its supertype.

// A static generic function that takes a type parameter T.
public static <T> ArrayList<T> filter(List<T> list, Predicate<? super T> predicate) {
  ArrayList<T> result = new ArrayList<>();
  for(T t : list) {
    if(predicate.test(t)) {  // Does item t pass the test predicate.test(t)?
      result.add(t);         // If it did, insert t into the result list.
    }
  }
  return result;
}

Below is an example of using filter to select words with a certain length (the example uses length 5) from a list of words.

// A word list for testing the "filter" function.
List<String> words = List.of("one", "two", "three", "four", "five", "six", "seven");
System.out.format("Original list: %s%n", words);
ArrayList<String> len5words = filter(words, w -> w.length() == 5);  // Test: is length 5?
System.out.format("Filtered list: %s%n", len5words);

This code would output:

Original list: [one, two, three, four, five, six, seven]
Filtered list: [three, seven]

The preceding example function could be developed further to e.g. allow the caller to define some kind of a transformation that should be applied to the items before they are put into the result. The example above added items simply by doing result.add(t), but we could now do something like result.add(mapper.apply(t)) instead. Here mapper would be a second function object received as a function parameter, and its member function apply would perform the item transformation. If we look at the previous list of Java functional interfaces, the interface Function would seem suitable: its function apply takes one parameter and returns a value. The function filterMap given below works in this manner. The type parameter T is the item type of the original list and R is the item type of the result list. The wildcard ? extends R of the parameter mapper is based on the fact that a list containing objects of type R can well store also objects of some subtype of R.

public static <T,R> ArrayList<R> filterMap(List<T> list, Predicate<? super T> predicate,
                                                    Function<? super T,? extends R> mapper) {
  ArrayList<R> result = new ArrayList<>();
  for(T t : list) {
    if(predicate.test(t)) {
      result.add(mapper.apply(t));   // Add t transformed by mapper into the result list.
    }
  }
  return result;
}

We could use filterMap e.g. to select words that represent integers and return them in an Integer list:

// Some words for testing, where some words represent integers.
List<String> words = List.of("one", "2", "three", "4", "five", "six", "7");
System.out.format("Original list: %s%n", words);
ArrayList<Integer> ints = filterMap(words, w -> {   // Predicate checks if represents a number.
    try{
      Integer.parseInt(w);
    }
    catch(NumberFormatException e){
      return false;
    }
    return true;
  },
  w -> Integer.parseInt(w));                        // Function transforms w into a number.
System.out.format("Filtered list: %s%n", ints);

This code would output:

Original list: [one, 2, three, 4, five, six, 7]
Filtered list: [2, 4, 7]

In Java a functional interface means

A function object is

The Java class library defines as an functional interface

Posting submission...