Function composition is a powerful mechanism for combining functions to build more complicated functions. Standard functions, operators, predicates, and consumers have methods for combining them (but not suppliers!).
Composing functions
In Java 8, the functional interface Function<T, R> has two default methods compose(...) and andThen(...) for composing new functions. The main difference between these methods is the execution order.
Generally, f.compose(g).apply(x) is the same as f(g(x)), and f.andThen(g).apply(x) is the same as g(f(x)).
Let's see an example with two functions: adder and multiplier.
Function<Integer, Integer> adder = x -> x + 10;
Function<Integer, Integer> multiplier = x -> x * 5;
// compose: adder(multiplier(5))
System.out.println("result: " + adder.compose(multiplier).apply(5));
// andThen: multiplier(adder(5))
System.out.println("result: " + adder.andThen(multiplier).apply(5));
In this case, method compose returns a composed function that first applies multiplier to its input, and then applies adder to the result. Method andThen returns a composed function that first applies adder to its input, and then applies a multiplier to the result.
The result of this program:
result: 35
result: 75
Operators can be combined in the same way.
Composing predicates
The functional interface IntPredicate (as well as other predicates) has three methods for composing new predicates: and(...), or(...) and negate(...). The meaning should be clear from the names.
There are two predicates: isOdd and lessThan11.
IntPredicate isOdd = n -> n % 2 != 0; // it's true for 1, 3, 5, 7, 9, 11 and so on
System.out.println(isOdd.test(10)); // prints "false"
System.out.println(isOdd.test(11)); // prints "true"
IntPredicate lessThan11 = n -> n < 11; // it's true for all numbers < 11
System.out.println(lessThan11.test(10)); // prints "true"
System.out.println(lessThan11.test(11)); // prints "false"
Let's negate the first predicate:
IntPredicate isEven = isOdd.negate(); // it's true for 0, 2, 4, 6, 8, 10 and so on
System.out.println(isEven.test(10)); // prints "true"
System.out.println(isEven.test(11)); // prints "false"
Let's combine both predicates:
IntPredicate isOddOrLessThan11 = isOdd.or(lessThan11);
System.out.println(isOddOrLessThan11.test(10)); // prints "true"
System.out.println(isOddOrLessThan11.test(11)); // prints "true"
System.out.println(isOddOrLessThan11.test(12)); // prints "false"
System.out.println(isOddOrLessThan11.test(13)); // prints "true"
IntPredicate isOddAndLessThan11 = isOdd.and(lessThan11);
System.out.println(isOddAndLessThan11.test(8)); // prints "false"
System.out.println(isOddAndLessThan11.test(9)); // prints "true"
System.out.println(isOddAndLessThan11.test(10)); // prints "false"
System.out.println(isOddAndLessThan11.test(11)); // prints "false"
So, functions composition allows building new functions using existing ones.