Predicate vs Function vs Consumer vs Supplier
Fast recall
Quick revision
Core answer
Whether the candidate can name and distinguish the four key functional interfaces
Confusing Function and Consumer — Function returns something, Consumer does not
Give a concrete real-world use case for each from Stream operations
Interview-ready guide
Interview answer
These four interfaces represent the four things you can do with data.
Predicate is for testing — it takes an input and returns true or false. You use it in filter operations, like filtering orders where the amount is greater than a hundred. Function is for transformation — it takes one type and returns another type.
You use it in map operations, like mapping a User to their email address. Consumer is for side effects — it takes an input but returns nothing. You use it when you want to do something with each element, like logging it or saving it to a database.
That's forEach. Supplier is for producing values — it takes no input and returns something. You use it for lazy initialization, like generating UUIDs only when needed.
The key difference freshers miss is between Function and Consumer. Function returns something, Consumer doesn't. If you're not using the return value, you probably need Consumer not Function.
In the Stream API, you'll see these everywhere: filter uses Predicate, map uses Function, forEach uses Consumer, and generate uses Supplier. Understanding these four makes the entire Streams API make sense.
Predicate vs Function vs Consumer vs Supplier
Direct Answer
Predicate takes T and returns boolean for testing. Function takes T and returns R for transformation. Consumer takes T and returns nothing for side effects. Supplier takes nothing and returns T for production. These four patterns cover the fundamental operations: test, transform, act, and produce.
Code Example
import java.util.function.*;
public class CoreFunctionalInterfaces {
public static void main(String[] args) {
Predicate<Integer> isEven = n -> n % 2 == 0; // T -> boolean
Function<Integer, Integer> square = n -> n * n; // T -> R
Consumer<String> printer = System.out::println; // T -> void
Supplier<String> greeting = () -> "hello"; // () -> T
System.out.println(isEven.test(4)); // true
System.out.println(square.apply(5)); // 25
printer.accept(greeting.get()); // hello
}
}
Common Mistakes & Pitfalls
- Using Function when Consumer is correct: Freshers often write lambdas that ignore the return value or return null, when they should use Consumer. If you're not returning anything meaningful, you need Consumer not Function.
- Confusing input/output patterns: Mixing up which interface takes what — especially confusing Supplier (no input) with Consumer (no output). The names help: Supplier supplies something, Consumer consumes something.
- Not using method references: Writing user -> user.getName() instead of User::getName when a simple method reference works. While both compile, the method reference is cleaner and shows you understand the connection between functional interfaces and existing methods.
Check your understanding
Answer these without looking back at the article.
- What is BiFunction and when would you use it?
- What is UnaryOperator?
- How do you chain Predicates in Java 8?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.