Predicate, Function, Consumer, Supplier — Java's Core Functional Interfaces
Fast recall
Quick revision
Core answer
java.util.function, each modeling a fundamental shape of computation: `Functionapply); `Predicateboolean (test); `Consumeraccept); `Supplierget). Together they cover most lambda use cases: Function for map, Predicate for filter, Consumer for forEach and side-effects, Supplier for lazy values and defaults (Optional.orElseGet, Map.computeIfAbsent). Each has combinators for composition: Function.andThen/compose, Predicate.and/or/negate, Consumer.andThen. They also have two-argument variants (BiFunction, BiPredicate, BiConsumer) and primitive specializations (IntFunction, ToIntFunction, IntPredicate, IntConsumer, IntSupplier) to avoid boxing in hot paths. The mental model: think of any computation as one of these four shapes, pick the matching interface, and chain combinators when you need composition. Knowing this vocabulary fluently is what makes Stream API code idiomatic.Whether the candidate knows the four basic interfaces, their signatures, the standard combinators, and when to use which one in stream operations and general code
Confusing Function and BiFunction signatures. Forgetting that Supplier takes no arguments. Using Consumer when Function is needed (or vice versa). Not knowing about primitive specializations and incurring boxing penalties
Demonstrate composition with combinators (Function.andThen, Predicate.and), mention `Predicate.not(...)` (Java 11+), discuss when to reach for primitive specializations to avoid boxing, and show how these map to the strategy pattern
Interview-ready guide
Interview answer
These four interfaces capture the four basic shapes of computation:
`Function
- Signature:
R apply(T t)— takes T, returns R - Used in
map - Example:
String::length,User::getName - Combinators:
andThen(this then next),compose(next then this),Function.identity() - Variants:
BiFunction,UnaryOperator(T to T),BinaryOperator(used in reduce)
`Predicate
- Signature:
boolean test(T t)— takes T, returns boolean - Used in
filter,anyMatch,allMatch,noneMatch - Example:
String::isEmpty,User::isActive - Combinators:
and,or,negate - Java 11+:
Predicate.not(String::isBlank)reads better thans -> !s.isBlank()
`Consumer
- Signature:
void accept(T t)— takes T, returns nothing - Used in
forEach,peek - Example:
System.out::println,repo::save - Combinator:
andThen(run this then the next) - Don't abuse — if you find yourself reaching for
forEachto mutate state, the imperative loop or a proper collector is usually clearer
`Supplier
- Signature:
T get()— takes nothing, returns T - Used in
Optional.orElseGet,Map.computeIfAbsent,Stream.generate, lazy defaults - Example:
ArrayList::new,UUID::randomUUID - Critical distinction:
Optional.orElse(expensiveDefault())calls the default eagerly;orElseGet(() -> expensiveDefault())is lazy
Two-argument variants
BiFunction,BiPredicate,BiConsumer- For three or more inputs, define a custom functional interface or use a record/tuple
Map.forEachtakes aBiConsumer
Primitive specializations — avoid boxing
IntFunction,ToIntFunction,IntUnaryOperator,IntPredicate,IntConsumer,IntSupplier- Same family for
LongandDouble - Use them in hot paths and when the API takes primitives
- Stream pipelines:
.mapToInt(...).sum()is faster than.map(...).reduce(0, Integer::sum)— zero boxing
How they map to stream operations
map(Function)— transformfilter(Predicate)— selectforEach(Consumer)— side effectpeek(Consumer)— debuggingStream.generate(Supplier)— infinite streamreduce(identity, BinaryOperator)— fold
Composition examples
Function:String::trimandThenString::toUpperCase→ trim then uppercasePredicate:isActive.and(isAdmin),isAdmin.negate(),Predicate.not(String::isBlank)Consumer:log.andThen(save)— log first, then saveSupplier: no built-in combinators — chain manually if needed
Common pitfalls
- Confusing
FunctionandBiFunction— watch the parameter count Consumerreturns void — useFunctionif you need a value backorElsevsorElseGet— eager vs lazy; useorElseGetfor expensive defaults- Boxing in hot paths — use primitive specializations
- Checked exceptions — standard interfaces don't declare them; wrap in
RuntimeExceptionor define a custom interface
Recommendation: Memorize these four signatures — they're the foundational vocabulary of modern Java. When designing an API or writing a stream, ask "which shape is this?" — transformation (Function), test (Predicate), side-effect (Consumer), or lazy producer (Supplier) — and pick the matching interface. Use combinators (andThen, and, or, negate, Predicate.not) instead of long lambda bodies; they read better and stay testable. Reach for primitive specializations (IntPredicate, ToIntFunction) in hot paths to avoid GC pressure from boxing. Always prefer `orElseGet` over `orElse` when the default is expensive to compute. And remember: if your lambda is more than a few lines, extract it to a named method — the symbol gives you a stack trace, a test target, and a place to document intent.
Predicate, Function, Consumer, Supplier — Java's Core Functional Interfaces
Direct Answer
These are the four core functional interfaces in java.util.function, each modeling a fundamental shape of computation: Function<T, R> transforms one value to another (apply); Predicate<T> tests a value and returns boolean (test); Consumer<T> accepts a value and does something with no return (accept); Supplier<T> takes nothing and produces a value (get). Together they cover most lambda use cases: Function for map, Predicate for filter, Consumer for forEach and side-effects, Supplier for lazy values and defaults (Optional.orElseGet, Map.computeIfAbsent). Each has combinators for composition: Function.andThen/compose, Predicate.and/or/negate, Consumer.andThen. They also have two-argument variants (BiFunction, BiPredicate, BiConsumer) and primitive specializations (IntFunction, ToIntFunction, IntPredicate, IntConsumer, IntSupplier) to avoid boxing in hot paths. The mental model: think of any computation as one of these four shapes, pick the matching interface, and chain combinators when you need composition. Knowing this vocabulary fluently is what makes Stream API code idiomatic.
Common Mistakes & Pitfalls
Confusing Function and BiFunction signatures. Forgetting that Supplier takes no arguments. Using Consumer when Function is needed (or vice versa). Not knowing about primitive specializations and incurring boxing penalties
Check your understanding
Answer these without looking back at the article.
- What's the difference between `Optional.orElse` and `Optional.orElseGet`?
- When would you reach for `IntFunction` instead of `Function<Integer, Integer>`?
- How would you compose three Predicates into a single complex condition?
- Why does `BinaryOperator<T>` exist when `BiFunction<T, T, T>` does the same thing?
- What's the difference between `Function.andThen` and `Function.compose`?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.