Skip to content

Supplier, Consumer, Predicate, Function

โ˜• Java 17 ยท The Ready-Made Lambda Toolkit ยท 1/4

Instead of one-off interfaces like MovieCheck or TitleMaker, Java offers generic ones in java.util.function. Every Java developer recognizes them at a glance.

Supplier โ€” no input, one output

public interface Supplier<T> { T get(); }

Supplier<LocalDate> today = LocalDate::now;
Supplier<List<Movie>> lists = ArrayList::new;

System.out.println(today.get());     // e.g. 2026-10-05
List<Movie> watchlist = lists.get(); // a new, empty list

A natural factory: each get() produces a value. Print today instead of today.get() and you get a generated name with $$Lambda: the lambda object, not its result.

Consumer โ€” input, nothing returned

public interface Consumer<T> { void accept(T t); }
public interface BiConsumer<T, U> { void accept(T t, U u); }

Consumer<String> show = System.out::println;
show.accept("Stranger Things");

var ratings = new HashMap<String, Double>();
BiConsumer<String, Double> rate = ratings::put;
rate.accept("Dark", 8.7);            // {Dark=8.7}

Bi means one more parameter: two inputs instead of one.

Predicate โ€” a yes-or-no question

public interface Predicate<T> { boolean test(T t); }
public interface BiPredicate<T, U> { boolean test(T t, U u); }

Predicate<String> blank = String::isBlank;
blank.test("  ");                                   // true

BiPredicate<String, String> starts = String::startsWith;
starts.test("Narcos", "Nar");                       // true

test() returns a primitive boolean, not a Boolean object.

Function โ€” turn one thing into another

public interface Function<T, R> { R apply(T t); }
public interface BiFunction<T, U, R> { R apply(T t, U u); }

Function<String, Integer> length = String::length;
length.apply("Wednesday");                          // 9

BiFunction<String, String, String> join = String::concat;
join.apply("Squid ", "Game");                       // Squid Game

The last type parameter is always the return type.

Operators โ€” same type in and out

UnaryOperator<String> shout = String::toUpperCase;  // a Function<String, String>
BinaryOperator<String> merge = String::concat;      // a BiFunction<String, String, String>

shout.apply("dark");                                // DARK
merge.apply("The ", "Crown");                       // The Crown

When every type is the same, the operator says it with one type parameter instead of two or three. The method is still apply().

Cheat sheet

Interface Method Returns
Supplier<T> get() T
Consumer<T> / BiConsumer<T,U> accept(T) / accept(T,U) void
Predicate<T> / BiPredicate<T,U> test(T) / test(T,U) boolean
Function<T,R> / BiFunction<T,U,R> apply(T) / apply(T,U) R
UnaryOperator<T> / BinaryOperator<T> apply(T) / apply(T,T) T

To pick one: no input โ†’ Supplier; nothing returned โ†’ Consumer; a boolean โ†’ Predicate; same type in and out โ†’ Operator; otherwise โ†’ Function. Two inputs โ†’ the Bi version (or BinaryOperator).

๐Ÿ’ป Code: Java17_Part_8_Functional_Programming on GitHub

Next: combining them โ†’