Modern Java

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.

Java 8+ (java.util.function); Java 21 (core language)

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