46. Functional Interfaces
Functional interfaces have exactly one abstract method; java.util.function provides the common ones — Function, Predicate, Supplier, Consumer, BiFunction — and they compose via andThen, compose, and, or, negate.
Explanation
Any interface with exactly one abstract method (plus any number of default and static methods) is a functional interface and can be used as a lambda target. @FunctionalInterface is an optional annotation that causes a compile error if the contract is violated.
Key types in java.util.function: - Function<T,R>: takes T, returns R. Compose with andThen (f then g) or compose (g then f). - Predicate<T>: takes T, returns boolean. Combine with and, or, negate. - Supplier<T>: takes nothing, returns T (lazy value producer). - Consumer<T>: takes T, returns nothing (side effects). - BiFunction<T,U,R>: takes T and U, returns R. - Primitive specialisations: IntFunction, ToIntFunction, IntSupplier, etc. — avoid boxing in hot paths.
Example
import java.util.List;
import java.util.function.*;
public class FunctionalInterfaces {
@FunctionalInterface
interface Transformer<T> { T transform(T input); } // custom FI
public static void main(String[] args) {
// Function<T,R> + compose/andThen
Function<String, String> trim = String::trim;
Function<String, String> upper = String::toUpperCase;
Function<String, Integer> length = String::length;
Function<String, Integer> trimThenLength = trim.andThen(length);
System.out.println(trimThenLength.apply(" hello ")); // 5
Function<String, String> upperThenTrim = trim.compose(upper); // upper THEN trim
System.out.println(upperThenTrim.apply(" hello ")); // " HELLO " then trimmed -> "HELLO"
// Predicate + and/or/negate
Predicate<String> nonEmpty = s -> !s.isEmpty();
Predicate<String> shortStr = s -> s.length() < 5;
Predicate<String> valid = nonEmpty.and(shortStr);
List.of("", "hi", "hello", "Java21").stream()
.filter(valid)
.forEach(System.out::println); // hi
// Supplier: lazy evaluation
Supplier<List<String>> listFactory = java.util.ArrayList::new;
List<String> newList = listFactory.get();
newList.add("lazy"); System.out.println(newList);
// Consumer + andThen
Consumer<String> print = System.out::println;
Consumer<String> log = s -> System.out.println("[LOG] " + s);
Consumer<String> both = print.andThen(log);
both.accept("event"); // prints twice
// BiFunction
BiFunction<String, Integer, String> repeat = (s, n) -> s.repeat(n);
System.out.println(repeat.apply("ab", 3)); // ababab
// Custom functional interface
Transformer<String> shout = s -> s.toUpperCase() + "!";
System.out.println(shout.transform("quiet")); // QUIET!
}
}Sources
- https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/function/package-summary.html — java.util.function
- https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html — Lambda expressions tutorial