javaintermediate

Functional Interfaces and Lambda Patterns

Use built-in and custom functional interfaces: Predicate, Function, Consumer, Supplier, and composition.

java
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

public class FunctionalDemo {

    // Predicate — test condition
    static final Predicate<String> isNotBlank = s -> s != null && !s.isBlank();
    static final Predicate<Integer> isAdult = age -> age >= 18;
    static final Predicate<Integer> isSenior = age -> age >= 65;

    // Compose predicates
    static final Predicate<Integer> isWorkingAge = isAdult.and(isSenior.negate());

    // Function — transform input
    static final Function<String, String> trim = String::trim;
    static final Function<String, String> lower = String::toLowerCase;
    static final Function<String, String> normalize = trim.andThen(lower);

    // BiFunction — two inputs
    static final BiFunction<String, String, String> fullName =
        (first, last) -> first + " " + last;

    // Custom functional interface
    @FunctionalInterface
    interface Validator<T> {
        boolean validate(T value);

        default Validator<T> and(Validator<T> other) {
            return t -> this.validate(t) && other.validate(t);
        }

        default Validator<T> or(Validator<T> other) {
            return t -> this.validate(t) || other.validate(t);
        }
    }

    // Pipeline pattern
    static <T> T pipe(T input, List<UnaryOperator<T>> transforms) {
        T result = input;
        for (var fn : transforms) {
            result = fn.apply(result);
        }
        return result;
    }

    public static void main(String[] args) {
        // Filter with composed predicate
        List<Integer> ages = List.of(15, 22, 35, 67, 70, 45);
        List<Integer> working = ages.stream()
            .filter(isWorkingAge)
            .toList();
        System.out.println(working); // [22, 35, 45]

        // Transform pipeline
        String result = normalize.apply("  Hello World  ");
        System.out.println(result); // hello world

        // Supplier — lazy initialization
        Supplier<List<String>> lazyList = ArrayList::new;

        // Consumer — side effects
        Consumer<String> logger = msg -> System.out.println("LOG: " + msg);
        Consumer<String> alerter = msg -> System.out.println("ALERT: " + msg);
        Consumer<String> both = logger.andThen(alerter);
        both.accept("Something happened");
    }
}

Use Cases

  • Composable validation and filtering logic
  • Data transformation pipelines
  • Callback-based API design

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.