Common Standard Functional Interfaces in Java
Java provides a set of reusable functional interfaces in:
Common Standard Functional Interfaces in Java
Java provides a set of reusable functional interfaces in:
java.util.function

Instead of creating a new functional interface every time, we can usually choose one of these standard interfaces.
They are especially important when working with Streams, lambdas, and method references.
1. Think in Terms of Inputs → Outputs
The easiest way to understand functional interfaces is to ask two questions:
How many inputs?
+
What does it return?
↓
Choose the Functional Interface
For example:
1 input → 1 output Function
1 input → no output Consumer
no input → 1 output Supplier
1 input → boolean Predicate
These four interfaces cover a huge number of everyday use cases.
2. Function<T, R> — Transform Something
A Function accepts one input and produces one output.
T ──────► Function ──────► R
Input Output
Its abstract method is:
R apply(T t);
For example:
Function<Product, String> getName = product -> product.getName();
Here:
Product ──► Function ──► String
Usage:
String name = getName.apply(product);
Typical use case
Transforming one value into another.
For example:
Product → String
String → Integer
User → UserDTO
Order → Invoice
You’ll see Function frequently with Stream's map() operation.
3. Consumer<T> — Do Something
A Consumer accepts one input but returns nothing.
T ──────► Consumer ──────► nothing
Its method is:
void accept(T t);
Example:
Consumer<Product> printProduct = product -> System.out.println(product);
Usage:
printProduct.accept(product);
Or using a method reference:
Consumer<Product> printProduct = System.out::println;
Typical use cases
A Consumer usually performs an action:
Input
│
├──► Print
├──► Log
├──► Save
├──► Send
└──► Update
A familiar example is:
products.forEach(System.out::println);
forEach() expects a Consumer.
4. Supplier<T> — Give Me Something
Supplier is essentially the opposite of Consumer.
It takes no input and produces one output.
nothing ──────► Supplier ──────► T
Its method is:
T get();
Example:
Supplier<Product> productSupplier = () -> new Product();
Usage:
Product product = productSupplier.get();
We could also use a constructor reference:
Supplier<Product> productSupplier = Product::new;
Typical use cases
Suppliers are useful for:
Factory creation
Lazy initialization
Default values
Object creation
Think:
“I don’t need anything from you — I’ll give you something.”
5. Predicate<T> — Ask a Question
A Predicate accepts one input and always returns a:
boolean
Visualize it as:
┌──► true
T ─► Predicate
└──► false
Its method is:
boolean test(T t);
Example:
Predicate<Product> isExpensive = product -> product.getPrice() > 10;
Usage:
boolean result = isExpensive.test(product);
Typical use case
Predicates answer yes/no questions:
Is expensive?
Is active?
Is valid?
Is empty?
Is available?
This makes them perfect for filtering:
products.stream()
.filter(product -> product.getPrice() > 10)
.toList();
In fact, the custom ProductFilter interface we created earlier had essentially the same shape as Predicate<Product>.
Instead of inventing:
interface ProductFilter {
boolean accept(Product product);
}
we could simply use:
Predicate<Product>
6. UnaryOperator<T> — Same Type In, Same Type Out
UnaryOperator<T> is a specialized version of:
Function<T, T>
The input and output must have the same type.
T ─────► UnaryOperator ─────► T
Its method is:
T apply(T t);
Example:
UnaryOperator<String> normalize = text -> text.trim();
Here:
String ──► String
Another example:
UnaryOperator<Integer> doubleIt = number -> number * 2;
Integer ──► Integer
Think:
Transform something without changing its type.
What Does Bi Mean?
So far, most interfaces accept one input.
Java also provides versions that accept two inputs.
The prefix:
Bi
simply means:
Two Inputs
This gives us:
Function → BiFunction
Consumer → BiConsumer
Predicate → BiPredicate
7. BiFunction<T, U, R>
BiFunction accepts two inputs and produces one output.
T ──┐
├──► BiFunction ──► R
U ──┘
Its method:
R apply(T t, U u);
Example:
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Usage:
int result = add.apply(10, 20);
Result:
10 ──┐
├──► add ──► 30
20 ──┘
8. BiConsumer<T, U>
Two inputs, no output:
T ──┐
├──► BiConsumer ──► nothing
U ──┘
Its method:
void accept(T t, U u);
Example:
BiConsumer<String, Integer> print =
(name, age) -> System.out.println(name + ": " + age);
Think:
Perform an action using two values.
9. BiPredicate<T, U>
Two inputs, one boolean result:
T ──┐
├──► BiPredicate ──► true / false
U ──┘
Its method:
boolean test(T t, U u);
Example:
BiPredicate<String, String> same = (a, b) -> a.equalsIgnoreCase(b);
Usage:
same.test("Java", "JAVA");
returns:
true
10. BinaryOperator<T>
BinaryOperator<T> is a specialized form of:
BiFunction<T, T, T>
Two values of the same type go in, and one value of that same type comes out.
T ──┐
├──► BinaryOperator ──► T
T ──┘
Its method:
T apply(T t1, T t2);
Example:
BinaryOperator<Integer> max = (a, b) -> Math.max(a, b);
Or:
BinaryOperator<Integer> add = (a, b) -> a + b;
Think of operations such as:
sum
max
min
combine
merge
Why Is There No BiSupplier?
You might notice:
BiFunction ✅
BiConsumer ✅
BiPredicate ✅
BiSupplier ❌
That’s because Bi means two inputs.
But a Supplier is defined by having:
0 inputs → 1 output
So a “two-input Supplier” wouldn’t really be a Supplier anymore.
The Big Picture
Here’s the easiest way to remember everything:
FUNCTION SHAPE
│
┌─────────────────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
1 INPUT 2 INPUTS 0 INPUT
│ │ │
│ │ ▼
│ │ Supplier
│ │ () → T
│ │
┌───────│──────────┐ ┌──────────┼────────────┐
▼ ▼ ▼ ▼ ▼ ▼
Function Consumer Predicate BiFunction BiConsumer BiPredicate
T→R T→void T→boolean T,U→R T,U→void T,U→boolean
And then we have the operators:
Function<T,T>
│
▼
UnaryOperator<T>
│
T → T
BiFunction<T,T,T>
│
▼
BinaryOperator<T>
│
T,T → T
Quick Reference Cheat Sheet

A Simple Memory Trick
Don’t memorize nine interfaces independently.
Start with these four:
Function → TRANSFORM
Consumer → DO
Supplier → PROVIDE
Predicate → TEST
Then add:
Bi → Two inputs
Operator → Same input/output type
So:
Bi + Function
↓
BiFunction
Bi + Consumer
↓
BiConsumer
Bi + Predicate
↓
BiPredicate
Function<T,T>
↓
UnaryOperator
BiFunction<T,T,T>
↓
BinaryOperator
That makes the whole java.util.function family much easier to remember.
Final Takeaway
When choosing a functional interface, don’t start by memorizing names.
Start by asking:
How many inputs do I have, and what do I need to return?
Input(s) + Output
↓
Function Shape
↓
Functional Interface
Once you learn to recognize the shape of the function, choosing between Function, Consumer, Supplier, Predicate, and their specialized versions becomes almost automatic.
Continue Learning Functional Interfaces🚀
← Previous: Understanding Functional Interfaces in Java | Next →: Practical Examples of Standard Functional Interfaces
메타데이터
- post_id
- fa3d5ce2e049
- slug
- common-standard-functional-interfaces-in-java-fa3d5ce2e049
- url
- https://medium.com/@surajk.explains/common-standard-functional-interfaces-in-java-fa3d5ce2e049
- canonical_url
- https://medium.com/@surajk.explains/common-standard-functional-interfaces-in-java-fa3d5ce2e049
- author_url
- https://medium.com/@surajk.explains
- status
- ok
- fetched_at
- 2026-09-03 04:44:24