Composing Functions

andThen, compose, and, or, negate

Functional interfaces have default methods to combine small functions into bigger ones, like LEGO blocks.

MethodOnMeaning
f.andThen(g)Function, Consumerrun f, then g on the result
f.compose(g)Functionrun g first, then f
p.and(q) / p.or(q)Predicatelogical AND / OR
p.negate(), Predicate.not(p)Predicatelogical NOT
Function.identity()Functionreturns its input unchanged
Comparator.comparing(...).thenComparing(...)Comparatormulti-key sorting

Building a Pipeline

Example

Clean up user input, then validate it with combined predicates.

import java.util.*;
import java.util.function.*;

public class Main {
    public static void main(String[] args) {
        Function<String, String> trim = String::trim;
        Function<String, String> lower = String::toLowerCase;
        Function<String, String> normalize = trim.andThen(lower);

        Predicate<String> notEmpty = s -> !s.isEmpty();
        Predicate<String> hasAt = s -> s.contains("@");
        Predicate<String> isGmail = s -> s.endsWith("@gmail.com");
        Predicate<String> validEmail = notEmpty.and(hasAt);

        List<String> inputs = List.of("  [email protected] ", "[email protected]", "   ", "no-at-sign");
        for (String in : inputs) {
            String email = normalize.apply(in);
            System.out.printf("%-20s valid=%-5b gmail=%b%n",
                    "'" + email + "'", validEmail.test(email), validEmail.and(isGmail).test(email));
        }

        Function<Integer, Integer> plus2 = x -> x + 2;
        Function<Integer, Integer> times3 = x -> x * 3;
        System.out.println("plus2.andThen(times3)(5) = " + plus2.andThen(times3).apply(5));
        System.out.println("plus2.compose(times3)(5) = " + plus2.compose(times3).apply(5));
    }
}

Output

'[email protected]'     valid=true  gmail=true
'[email protected]'  valid=true  gmail=false
''                   valid=false gmail=false
'no-at-sign'         valid=false gmail=false
plus2.andThen(times3)(5) = 21
plus2.compose(times3)(5) = 17

Practice

Exercise: Using Predicate.not, remove blank strings from List.of("a", " ", "b", "").

Show answer
List<String> result = List.of("a", " ", "b", "").stream()
        .filter(Predicate.not(String::isBlank))
        .toList();
System.out.println(result); // [a, b]