Several built-in functional interfaces provide default methods like andThen, compose, and the Predicate combinators and/or/negate. What do these do, and how does composition order differ between Function.andThen and Function.compose?
answer
- andThen = this first, arg after (left→right)
- compose = arg first, this after (right→left, math f∘g)
- f.andThen(g) == g.compose(f)
- Consumer has andThen only; Supplier has no combinators
- Predicate: and/or short-circuit, negate, static not (Java 11)
basics
~20 sThese default methods let you combine small functions into bigger ones without writing glue code. Function.andThen(g) runs this first then g; Function.compose(g) runs g first then this. Predicate.and/or/negate combine boolean tests; Consumer.andThen runs two consumers in sequence.
solid answer
~50 sThe core interfaces ship default (already-implemented) methods so you can build pipelines declaratively. For Function, the difference is order: f.andThen(g) means 'apply f, then feed its result to g' — left to right — whereas f.compose(g) means 'apply g first, then f' — right to left, matching mathematical g∘f notation. So f.andThen(g) equals g.compose(f). Consumer has andThen, which chains side effects: c1.andThen(c2) runs c1 then c2 on the same input. Predicate offers the boolean combinators and(p), or(p), and negate(), which build compound conditions with normal short-circuit semantics, plus a static isEqual and (since Java 11) not. These let you compose reusable predicates instead of writing nested boolean expressions. UnaryOperator/BinaryOperator inherit Function's andThen/compose. Suppliers have no composition methods because there is nothing to chain into. Using these keeps transformation and filtering logic small, named, and testable.
code
java · 14 linesFunction<Integer,Integer> plus1 = x -> x + 1;
Function<Integer,Integer> times2 = x -> x * 2;
plus1.andThen(times2).apply(3); // (3+1)*2 = 8 -> add then double
plus1.compose(times2).apply(3); // (3*2)+1 = 7 -> double then add
// equivalence: plus1.andThen(times2) == times2.compose(plus1)
Predicate<String> notBlank = Predicate.not(String::isBlank);
Predicate<String> shortish = s -> s.length() < 10;
boolean ok = notBlank.and(shortish).test("hello"); // true, short-circuits
Consumer<String> log = System.out::println;
Consumer<String> store = s -> {/* save */};
log.andThen(store).accept("x"); // log first, then store, same inputgo deeper
Knows these default methods chain functions/predicates and that andThen runs the current function first.
States the andThen-vs-compose order precisely, gives a numeric example, and lists Predicate's and/or/negate with short-circuit behavior.
Explains the andThen/compose equivalence identity, why Consumer/Supplier differ, exception propagation in chains, and when composition aids vs hurts readability.
Frames combinators as the JDK's lightweight take on function composition, advises on reusable-predicate design, and reasons about pipeline readability and testability trade-offs at scale.
## Why default methods exist here A **default method** is a method with a body declared in an interface (introduced in Java 8). It lets an interface ship reusable behavior without breaking implementers. The functional interfaces use defaults to provide **combinators** — methods that take another function and return a *new* combined function — so you can assemble behavior declaratively rather than nesting lambdas by hand. ## Function: andThen vs compose (the order question) `Function<T,R>` has two combinators, and the classic interview point is their **order**: - **`f.andThen(g)`** — apply `f` to the input, then pass `f`'s result to `g`. Reads **left to right**: input → f → g → output. Type-wise, `f` is `Function<T,R>`, `g` is `Function<R,V>`, the result is `Function<T,V>`. - **`f.compose(g)`** — apply `g` first, then pass its result to `f`. Reads **right to left**, matching the math notation **(f ∘ g)(x) = f(g(x))**. Here `g` is `Function<T,R>`, `f` is `Function<R,V>`. The identity to remember: **`f.andThen(g)` is exactly equivalent to `g.compose(f)`** — they describe the same pipeline from opposite ends. Concrete example with `Function<Integer,Integer>` `plus1 = x -> x+1` and `times2 = x -> x*2`: - `plus1.andThen(times2).apply(3)` → `(3+1)*2` = **8** (add first, then double). - `plus1.compose(times2).apply(3)` → `(3*2)+1` = **7** (double first, then add). The result types differ, so a chain whose intermediate types don't line up won't compile — the compiler enforces the flow. ## Consumer.andThen `Consumer<T>` has only `andThen` (no `compose`, since a consumer returns nothing to feed onward). `c1.andThen(c2)` returns a consumer that calls `c1.accept(x)` **then** `c2.accept(x)` on the **same** input `x`, in order. Useful to run several side effects (log, then store) as one consumer. If `c1` throws, `c2` is not run. ## Predicate combinators `Predicate<T>` provides: - **`and(other)`** — logical AND with **short-circuit**: if `this.test(x)` is false, `other` is not evaluated. - **`or(other)`** — logical OR with short-circuit: if `this.test(x)` is true, `other` is not evaluated. - **`negate()`** — logical NOT of this predicate. - static **`isEqual(target)`** — a predicate testing `Objects.equals(target, x)`. - static **`Predicate.not(p)`** (Java 11+) — negates a predicate, handy with method references: `Predicate.not(String::isBlank)`. These let you name building-block predicates (`isActive`, `isAdult`) and combine them: `isActive.and(isAdult).or(isAdmin)`. ## Operators and Bi- variants `UnaryOperator` and `BinaryOperator` **inherit** `Function`/`BiFunction` defaults. `BiFunction` has `andThen` (its result feeds a one-arg `Function`) but **no `compose`** (you cannot pre-compose two inputs cleanly). `BiPredicate` has `and`/`or`/`negate`. `Supplier` has **no** combinators — there is nothing upstream to chain. ## Trade-off and good practice Composition keeps each piece small, named, reusable, and unit-testable, and reads as a pipeline. Overusing it — deeply nested `andThen/compose` chains — can become harder to read than a plain method; use it where the pieces are genuinely reusable. Each combinator returns a **new** function and does not mutate the originals. ## Deriving an answer Whenever asked 'which runs first', map to position in the name: **andThen** = *and then* the argument runs *after*; **compose** = the argument is composed *before* (math order). And recall the bridge identity `a.andThen(b) == b.compose(a)`.
- Given f.andThen(g), write the equivalent using compose.g.compose(f). Both produce the pipeline input → f → g → output; andThen describes it forward, compose describes it backward.
- Why does Consumer have andThen but not compose, and Supplier neither?Consumer returns void, so there is no result to pass forward to a second stage's input — but you can still run two consumers in sequence on the same input (andThen). Supplier takes no input and is the start of a pipeline, so it has nothing to compose with on either side.
saying these in an interview costs you the question
- Swapping andThen and compose order
- Claiming combinators mutate the original functions — they return new ones
- Expecting Function.compose on Consumer or Supplier — they don't have it
- Forgetting Predicate.and/or short-circuit just like && and ||
- Thinking BiFunction has compose — it only has andThen