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
.
A 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!