Built-In Functional Interfaces in Java
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
| Interface | Method | Takes | Returns | Typical use |
|---|---|---|---|---|
Predicate<T> | boolean test(T t) | One value | boolean | Filtering: "is it valid?" |
Function<T, R> | R apply(T t) | One value | Another value | Transforming: name to length |
Consumer<T> | void accept(T t) | One value | Nothing | Doing something: printing |
Supplier<T> | T get() | Nothing | A value | Creating values on demand |
UnaryOperator<T> | T apply(T t) | One value | Same type | Changing a value: to upper case |
BinaryOperator<T> | T apply(T a, T b) | Two values | Same type | Combining: adding two numbers |
BiFunction<T, U, R> | R apply(T t, U u) | Two values | A value | Combining different types |
There are also BiPredicate<T, U> and BiConsumer<T, U>, which take two arguments.
Example: Predicate, Function, Consumer and Supplier
Let's see an example of the four core interfaces.
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());
}
}38 passed? false
Length of Srihitha: 8
Welcome, Advitha
Exam on 2026-12-01Explanation: 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
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));
}
}NAVIKA!
91 + 78 = 169
Larger: 91
abababExplanation: 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 method | Interface 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
- Easy: Write a
Predicate<String>that checks whether a string is a valid 6-digit PIN code, and test it on three inputs. - 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.
Key Takeaways
Was this tutorial helpful?