← Back to list

Common Standard Functional Interfaces in Java

Java provides a set of reusable functional interfaces in:

Suraj · 2026-08-25 19:08 · 0 claps · 4.3 min read
#java-functional-interface #modern-java #javafunctionalprogramming #clean-code #java-stream-api
Open on Medium ↗
Wiki topics: 💻 · Programming

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