Built-In Functional Interfaces in Java

Naveen AnamaneniUpdated · 5 min read

Java provides a set of ready-made functional interfaces in the java.util.function package, so you rarely need to write your own. Each one describes a common shape of function: something that tests a value, transforms it, uses it, supplies it, or combines two values. The Stream API and many collection methods are built on them. In this lesson, you will learn the seven most important ones with examples.

The Main Built-In Functional Interfaces

InterfaceMethodTakesReturnsTypical use
Predicate<T>boolean test(T t)One valuebooleanFiltering: "is it valid?"
Function<T, R>R apply(T t)One valueAnother valueTransforming: name to length
Consumer<T>void accept(T t)One valueNothingDoing something: printing
Supplier<T>T get()NothingA valueCreating values on demand
UnaryOperator<T>T apply(T t)One valueSame typeChanging a value: to upper case
BinaryOperator<T>T apply(T a, T b)Two valuesSame typeCombining: adding two numbers
BiFunction<T, U, R>R apply(T t, U u)Two valuesA valueCombining different types

There are also BiPredicate<T, U> and BiConsumer<T, U>, which take two arguments.

Each interface is a different shape: what goes in and what comes out

Example: Predicate, Function, Consumer and Supplier

Let's see an example of the four core interfaces.

CoreFour.java▶ Try it
import java.time.LocalDate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
 
public class CoreFour {
    public static void main(String[] args) {
        Predicate<Integer> passed = m -> m >= 40;
        System.out.println("38 passed? " + passed.test(38));
 
        Function<String, Integer> length = s -> s.length();
        System.out.println("Length of Srihitha: " + length.apply("Srihitha"));
 
        Consumer<String> greet = name -> System.out.println("Welcome, " + name);
        greet.accept("Advitha");
 
        Supplier<LocalDate> examDay = () -> LocalDate.of(2026, 12, 1);
        System.out.println("Exam on " + examDay.get());
    }
}
Output
38 passed? false
Length of Srihitha: 8
Welcome, Advitha
Exam on 2026-12-01

Explanation: The Predicate tests a mark, the Function turns a string into its length, the Consumer performs an action and returns nothing, and the Supplier produces a value with no input. Each is called through its own method name: test, apply, accept and get.

Example: Operators and BiFunction

Operators.java▶ Try it
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
 
public class Operators {
    public static void main(String[] args) {
        UnaryOperator<String> shout = s -> s.toUpperCase() + "!";
        System.out.println(shout.apply("navika"));
 
        BinaryOperator<Integer> add = (a, b) -> a + b;
        System.out.println("91 + 78 = " + add.apply(91, 78));
 
        BinaryOperator<Integer> larger = BinaryOperator.maxBy(Integer::compare);
        System.out.println("Larger: " + larger.apply(91, 78));
 
        BiFunction<String, Integer, String> repeat = (s, n) -> s.repeat(n);
        System.out.println(repeat.apply("ab", 3));
    }
}
Output
NAVIKA!
91 + 78 = 169
Larger: 91
ababab

Explanation: UnaryOperator<T> is a Function<T, T>: same type in and out. BinaryOperator<T> combines two values of the same type; maxBy builds one from a comparator. BiFunction takes two values of different types, here a string and a count.

Where They Are Used

JDK methodInterface it takes
list.removeIf(...), stream.filter(...)Predicate
stream.map(...), map.computeIfAbsent(...)Function
list.forEach(...), stream.forEach(...)Consumer
optional.orElseGet(...), Stream.generate(...)Supplier
list.replaceAll(...)UnaryOperator
stream.reduce(...), map.merge(...)BinaryOperator / BiFunction

Common Mistakes

Calling the wrong method name, such as predicate.apply(x). Predicate uses test, Consumer uses accept, Supplier uses get.

Using Function<Integer, Integer> where a primitive version fits. Boxing costs time in hot code; see "Primitive Functional Interfaces in Java".

Returning a value from a Consumer lambda. A consumer's result is ignored.

Interview Corner

What is the difference between Predicate, Function, Consumer and Supplier?

They are functional interfaces in java.util.function with different shapes. Predicate<T> takes a value and returns a boolean through test. Function<T, R> takes a value and returns a result through apply. Consumer<T> takes a value and returns nothing through accept, so it's used for side effects. Supplier<T> takes nothing and returns a value through get, often used for lazy or default values.

Practice

  1. Easy: Write a Predicate<String> that checks whether a string is a valid 6-digit PIN code, and test it on three inputs.
  2. Harder: Write a method List<R> transform(List<T> list, Predicate<T> keep, Function<T, R> change) that returns the changed values of the elements that pass the test, using a loop.

Quick check: Built-in functional interfaces

4 questions
1Which interface returns a boolean?
2Which interface takes no input?
3What is UnaryOperator?
4Which method does Consumer have?

Key Takeaways

Was this tutorial helpful?

Lesson 307 of 482