Composing Functions in Java
Composing functions means joining small functions together to build a bigger one. Java's functional interfaces have default methods for this: Function has andThen and compose, Predicate has and, or and negate, and Consumer has andThen. Instead of writing one large lambda, you write several small, well-named pieces and combine them. In this lesson, you will learn each composition method with an example.
Composition Methods
| Interface | Method | Description |
|---|---|---|
Function<T, R> | andThen(after) | It applies this function, then after to the result. |
Function<T, R> | compose(before) | It applies before first, then this function. |
Function | static identity() | It returns a function that returns its input unchanged. |
Predicate<T> | and(other) | It is true only if both predicates are true. |
Predicate<T> | or(other) | It is true if either predicate is true. |
Predicate<T> | negate() | It reverses the result. |
Predicate | static not(p) | It returns the negation of p (Java 11). |
Consumer<T> | andThen(after) | It runs this consumer, then after, on the same value. |
Example: andThen and compose
Let's see an example that shows the order in which composed functions run.
import java.util.function.Function;
public class FunctionChain {
public static void main(String[] args) {
Function<Integer, Integer> addTen = x -> x + 10;
Function<Integer, Integer> doubleIt = x -> x * 2;
System.out.println("addTen.andThen(doubleIt): "
+ addTen.andThen(doubleIt).apply(5));
System.out.println("addTen.compose(doubleIt): "
+ addTen.compose(doubleIt).apply(5));
Function<String, String> trim = String::strip;
Function<String, String> clean = trim.andThen(String::toLowerCase)
.andThen(s -> s.replace(' ', '-'));
System.out.println(clean.apply(" Navika Anamaneni "));
}
}addTen.andThen(doubleIt): 30
addTen.compose(doubleIt): 20
navika-anamaneniExplanation: addTen.andThen(doubleIt) runs addTen first: (5 + 10) x 2 = 30. addTen.compose(doubleIt) runs doubleIt first: (5 x 2) + 10 = 20. The clean function chains three small steps into a "slug maker" that trims, lowercases and replaces spaces.
Example: Combining Predicates
import java.util.List;
import java.util.function.Predicate;
public class PredicateChain {
public static void main(String[] args) {
Predicate<String> startsWithA = s -> s.startsWith("A");
Predicate<String> longName = s -> s.length() > 7;
List<String> names =
List.of("Navika", "Srihitha", "Advitha", "Anamaneni");
System.out.println("A and long: "
+ names.stream().filter(startsWithA.and(longName)).toList());
System.out.println("A or long: "
+ names.stream().filter(startsWithA.or(longName)).toList());
System.out.println("not A: "
+ names.stream().filter(startsWithA.negate()).toList());
System.out.println("not long: "
+ names.stream().filter(Predicate.not(longName)).toList());
}
}A and long: [Anamaneni]
A or long: [Srihitha, Advitha, Anamaneni]
not A: [Navika, Srihitha]
not long: [Navika, Advitha]Explanation: and keeps names that start with A and have more than 7 letters, which is only Anamaneni. or keeps names that satisfy either test. negate() and Predicate.not(...) both flip a test; not is handy with method references, as in filter(Predicate.not(String::isBlank)).
Example: Chaining Consumers
import java.util.function.Consumer;
public class ConsumerChain {
public static void main(String[] args) {
Consumer<String> log = s -> System.out.println("LOG: " + s);
Consumer<String> greet = s -> System.out.println("Hello, " + s);
log.andThen(greet).accept("Advitha");
}
}LOG: Advitha
Hello, AdvithaExplanation: log.andThen(greet) creates one consumer that runs log and then greet with the same input.
Common Mistakes
Mixing up andThen and compose. Read andThen left to right; compose right to left.
Composing predicates with different types. Predicate<String>.and(Predicate<Integer>) doesn't compile.
Forgetting that Function.identity() is a function, not a value. Use it where a Function is expected, such as toMap(Function.identity(), ...).
Practice
- Easy: Compose
Function<Double, Double>steps that add 18% tax and then subtract a discount of 50, and apply them to a price. - Harder: Build predicates
isAdult,hasTicketandisVipfor a recordGuest(String name, int age, boolean ticket, boolean vip), and combine them so that adults with a ticket, or any VIP, may enter.
Key Takeaways
Was this tutorial helpful?