Composing Predicates and Functions

Streams · lesson 30 of 42 · 3 min read

Build one lambda from several with and, or, negate, andThen and compose.

Open this lesson in the learning hub

Key points

  • Predicate.and, or and negate build a compound test without a nested lambda.
  • and short-circuits exactly like &&, so put the cheapest test first.
  • Predicate.not(...) (Java 11+) negates a method reference: not(String::isBlank).
  • f.andThen(g) runs f first. f.compose(g) runs g first. That swap is the classic slip.
  • Naming the parts turns a wall of anonymous filter lambdas into a line you can read out loud.

Example

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

public class Main {
    public static void main(String[] args) {
        Predicate<String> notBlank = Predicate.not(String::isBlank);
        Predicate<String> shortName = s -> s.length() <= 4;
        Predicate<String> startsA = s -> s.startsWith("a");

        List<String> raw = List.of("ada", " ", "alan", "grace", "amelia");

        System.out.println("and     : " + raw.stream().filter(notBlank.and(startsA)).toList());
        System.out.println("or      : " + raw.stream().filter(shortName.or(startsA)).toList());
        System.out.println("negate  : " + raw.stream().filter(startsA.negate().and(notBlank)).toList());

        // andThen runs this one first; compose runs the argument first.
        Function<Integer, Integer> doubled = n -> n * 2;
        Function<Integer, Integer> plusOne = n -> n + 1;
        System.out.println("andThen : " + doubled.andThen(plusOne).apply(5) + "   (double, then add)");
        System.out.println("compose : " + doubled.compose(plusOne).apply(5) + "   (add, then double)");

        Function<String, String> trim = String::trim;
        System.out.println("chained : "
                + trim.andThen(String::toUpperCase).andThen(String::length).apply(" grace "));

        // Comparators compose in exactly the same style.
        System.out.println("sorted  : " + raw.stream().filter(notBlank)
                .sorted(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()))
                .toList());
    }
}

Name your predicates, compose them, and the filter line stops needing a comment.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.