Higher-order Functions in Java

stylised image of woman with brain shown and a question mark

This is a term you may not have heard. Although the term isn’t used often, these kinds of functions are common. We use higher-order functions all the time when we use streams.

Higher-order functions are functions that either accept other functions as arguments, or return a function as a result.

The result of one higher-order function can be used as the input to another higher-order function. Any time we pass a lambda expression to a method, that method is a higher-order function.

Java 8 supports higher-order functions, along with lambda expressions and functional interfaces. Stream processing uses lambda expressions and higher-order functions extensively.

Example

Previously I told you about the standard functional interfaces in the java.util.function package. One of the most commonly used functional interfaces is the Predicate.

Predicate represents a function that takes one argument and returns a boolean value. The functional method is boolean test(Object). So the interface looks like this (omitting default and static methods):

@FunctionalInterface
public interface Predicate<T> {
public boolean test(T t);
}

We create Predicate variables and assign lambda expressions to them as follows:

Predicate<Employee> pred1 = e -> e.getAge() < 65;
Predicate<Employee> pred2 = e -> e.getSalary() > 10000.0;
// using a default method of Predicate
Predicate<Employee> pred3 = pred1.and(pred2);

But what about the test() method? We never create one. And we do not call it in the normal course of programming, for example, when processing streams.

// create and populate a list with employee objects...
List<Employee> list = new ArrayList<>();
...
// use a stream to process the list
list.stream().filter(p1).filter(p2).forEach(
e -> System.out.println(e));

This filter() method is a higher-order function that takes a Predicate as a parameter. The filter() method is applied to each element to check if it should be included in the stream. Inside the filter() method, the test() method of the Predicate is called, and the lambda expression is executed.

Writing our own higher-order function

Let’s try it out by creating our own higher-order function that takes a Predicate:

public void runPredicate(Employee e, Predicate<Employee> p) {
    if (p.test(e) == true)
        System.out.println("True");
    else 
        System.out.println("False");
}

We can call the higher-order function as follows:

Employee e1 = new Employee("Harriet", 31, 
Person.FEMALE, 30_000.0);
Predicate<Employee> pred1 = e -> e.getAge() < 65;
Predicate<Employee> pred2 = e -> e.getSalary() > 10000.0;
// using a default method of Predicate
Predicate<Employee> pred3 = pred1.and(pred2);

runPredicate(e1, pred1);
runPredicate(e1, pred2);
runPredicate(e1, pred3);

There it is! Simple as that!

Leave a Comment

Your email address will not be published. Required fields are marked *