skip to content

Function & Predicate Composition

Function.andThen versus compose (the argument order flips), Consumer.andThen, and Predicate.and, or and negate. These are default methods, which is itself a nice illustration of why default methods were added.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

In Java, what is the difference between Function.andThen and Function.compose? Given f.andThen(g) versus f.compose(g), which function runs first?

level: juniorimportance: must knowfreq 70%

answer

  1. andThen = receiver runs first (reading order)
  2. compose = argument runs first (math f∘g, right-to-left)
  3. a.andThen(b) == b.compose(a)
  4. Lazy: only builds a new Function; runs on .apply
  5. Types must chain (output feeds next input)

basics

~10 s

Both glue two functions into one. f.andThen(g) runs f first, then feeds its result to g. f.compose(g) runs g first, then feeds its result to f. So andThen reads left-to-right; compose reads right-to-left.

solid answer

~40 s

Function<T,R> has two default combinators that build a new Function without running anything. f.andThen(g) returns a function that applies f, then passes f's output into g: x -> g(f(x)). f.compose(g) is the reverse: it applies g first, then f: x -> f(g(x)). The mnemonic is that andThen executes in reading order (the receiver runs first), while compose mirrors the math notation f∘g where the inner function runs first. Types must line up: in andThen, f's output type must match g's input type; in compose, g's output must match f's input. Both are lazy — calling them only constructs a composed Function; nothing executes until you call .apply on the result. Neither mutates f or g; they are pure factory methods.

go deeper

for a junior

Knows andThen runs the receiver first and compose runs the argument first, and can predict the output of a simple chain.

for a middle

Explains the equivalence a.andThen(b) == b.compose(a), the type-chaining requirement, and that composition is lazy (builds a Function, runs on apply).

for a senior

Discusses readability trade-offs (prefer andThen for execution order), wildcard variance in the signatures, and uses composition to build reusable transformation pipelines instead of nested calls.

for a principal

Frames composition as a building block for declarative pipelines, weighs it against Stream.map chaining and method extraction for maintainability, and notes purity/no-mutation guarantees that make composed functions safely shareable.

## Background: what a Function is In Java, `java.util.function.Function<T, R>` is a **functional interface** — an interface with exactly one abstract method, here `R apply(T t)`. It represents a transformation: give it a `T`, get back an `R`. Because it has a single abstract method, you can supply it with a lambda (`x -> x + 1`) or a method reference (`String::length`). **Composition** means combining two simple functions into a single new function, so that the output of one becomes the input of the other. `Function` provides two **default methods** (concrete methods that live on the interface itself) to do this: `andThen` and `compose`. ## andThen — run the receiver first ``` default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { return (T t) -> after.apply(this.apply(t)); } ``` `f.andThen(g)` returns a brand-new `Function`. When that new function is later applied to a value `x`, it computes `g(f(x))` — **f runs first**, then its result is handed to g. Read it left-to-right: "apply f, **and then** apply g." Example: `Function<Integer,Integer> times2 = n -> n*2; Function<Integer,Integer> plus3 = n -> n+3;` - `times2.andThen(plus3).apply(5)` = `plus3(times2(5))` = `plus3(10)` = `13`. ## compose — run the argument first ``` default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { return (V v) -> this.apply(before.apply(v)); } ``` `f.compose(g)` also returns a new `Function`, but it computes `f(g(x))` — **g (the argument) runs first**. This mirrors mathematical composition `f ∘ g`, which is read right-to-left. - `times2.compose(plus3).apply(5)` = `times2(plus3(5))` = `times2(8)` = `16`. Notice `13 ≠ 16`: order matters whenever the two functions don't commute. ## The relationship `a.andThen(b)` is exactly the same as `b.compose(a)`. They are two views of the same operation; which you pick is a readability choice. Most people prefer `andThen` because it reads in execution order. ## Type alignment - For `f.andThen(g)`: f's **output** type feeds g's **input** type. If `f: T->R`, then `g: R->V`, result `T->V`. - For `f.compose(g)`: g's **output** type feeds f's **input** type. If `f: T->R`, then `g: V->T`, result `V->R`. The wildcards (`? super R`, `? extends V`) just make the methods flexible about super/subtypes; conceptually the types must chain. ## Laziness and purity Both methods are **lazy builders**: calling `andThen`/`compose` does no computation — it only constructs a new `Function` object. The actual work happens later when you call `.apply(...)` on the composed result. They also never mutate the originals; `f` and `g` are untouched, so you can reuse them in other compositions. ## Why it matters Composition lets you build small, testable, single-purpose functions and assemble pipelines without nesting `g(f(x))` by hand or writing intermediate variables. It's the functional analogue of chaining and is heavily used with the Streams API (`map`, `Collectors`) and in configuration/transformation code.

  • Rewrite f.compose(g) using andThen.
    g.andThen(f) — they are equivalent; both compute f(g(x)).
  • If f.andThen(g) and f.compose(g) give the same result for some f and g, what does that tell you?
    That f and g commute under composition (f(g(x)) == g(f(x)) for the inputs used) — e.g. both are 'add a constant'. It's not generally true.

saying these in an interview costs you the question

  • Saying compose runs the receiver first — it runs the ARGUMENT first.
  • Claiming the methods execute the functions immediately — they only build a composed Function.
  • Thinking andThen and compose give the same result for any pair of functions — order matters unless they commute.
  • Believing andThen/compose mutate the original functions.

context

open as a page

How do you combine Predicates in Java using and, or, and negate, and what do these combinators return?

level: juniorimportance: must knowfreq 65%

basics

~10 s

Predicate is a function returning true/false. p.and(q) is true only if both are true; p.or(q) is true if either is true; p.negate() flips the result. Each returns a new Predicate you can keep combining.

open as a page

What does Consumer.andThen do, and how does composing Consumers differ from composing Functions?

level: middleimportance: should knowfreq 45%

basics

~20 s

Consumer takes a value and returns nothing (it does a side effect). c1.andThen(c2) makes one Consumer that runs c1 then c2 on the SAME input — it doesn't pass a result along, because Consumers produce no result.

open as a page

Why are andThen, compose, and and/or/negate implemented as default methods on the functional interfaces, and what does that imply for lambdas and custom implementations?

level: seniorimportance: should knowfreq 35%

basics

~10 s

They're default methods — concrete methods written on the interface itself. That keeps the single abstract method intact (so lambdas still work) while giving every Function/Predicate/Consumer ready-made combinators for free, without an extra class.

open as a page

When building a transformation pipeline, when would you compose Function objects with andThen/compose versus chaining Stream.map calls, and what trade-offs guide the choice?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Use Function composition (andThen/compose) to build a single reusable transform you can name, pass around, test, and reuse in many places. Use Stream.map chaining when the steps are inline stages of a one-off data flow. Both produce the same result; one is a reusable value, the other is in-place pipeline structure.

open as a page