Fundamentals 10 min read

Master Java Functional Interfaces: Practical Guide to Comparator, Predicate, and Function

This article explains Java's functional interfaces—Predicate, Function, Consumer, Supplier, and Comparator—showing their method signatures, how to compose them, custom interface creation, common pitfalls, and concise code examples for everyday use.

CodeNotes
CodeNotes
CodeNotes
Master Java Functional Interfaces: Practical Guide to Comparator, Predicate, and Function

Functional Interfaces Overview

Java 8 introduced lambda expressions; a functional interface is an interface that contains exactly one abstract method and can be instantiated with a lambda expression.

Core Interfaces in java.util.function

Predicate<T>

– method boolean test(T t) – evaluates a condition and returns a boolean. Function<T,R> – method R apply(T t) – transforms a value of type T to type R. Consumer<T> – method void accept(T t) – consumes a value without returning a result. Supplier<T> – method T get() – provides a value without any input parameters.

1. Predicate – Conditional Checks

import java.util.function.Predicate;
import java.util.List;
import java.util.stream.Collectors;

public class PredicateDemo {
    public static void main(String[] args) {
        // Define predicate conditions
        Predicate<String> isLong = s -> s.length() > 5;
        Predicate<String> startWithJ = s -> s.startsWith("J");

        System.out.println(isLong.test("Java"));          // false
        System.out.println(isLong.test("JavaScript"));   // true

        // Combine predicates
        Predicate<String> combined = isLong.and(startWithJ);
        System.out.println(combined.test("JavaScript")); // true (long and starts with J)
        System.out.println(combined.test("Python"));    // false (does not start with J)

        Predicate<String> notLong = isLong.negate();
        System.out.println(notLong.test("Go"));          // true (not long)

        // Real use case: filter a collection
        List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Predicate<Integer> greaterThan5 = n -> n > 5;

        List<Integer> result = numbers.stream()
            .filter(isEven.and(greaterThan5))
            .collect(Collectors.toList());
        System.out.println(result); // [6, 8, 10]
    }
}

2. Function – Data Transformation

import java.util.function.Function;

public class FunctionDemo {
    public static void main(String[] args) {
        // String to integer
        Function<String, Integer> parseInt = Integer::parseInt;
        System.out.println(parseInt.apply("42")); // 42

        // Integer to formatted string
        Function<Integer, String> intToStr = n -> "Number: " + n;
        System.out.println(intToStr.apply(100)); // Number: 100

        // andThen – forward composition (f then g)
        Function<String, String> trim = String::trim;
        Function<String, String> upper = String::toUpperCase;
        Function<String, String> pipeline = trim.andThen(upper);
        System.out.println(pipeline.apply("  hello world  ")); // HELLO WORLD

        // compose – reverse composition (g then f)
        Function<Integer, Integer> times2 = x -> x * 2;
        Function<Integer, Integer> plus3 = x -> x + 3;
        System.out.println(times2.compose(plus3).apply(5)); // (5+3)*2 = 16
        System.out.println(times2.andThen(plus3).apply(5)); // 5*2+3 = 13
    }
}

3. Consumer – Consuming Data

import java.util.function.Consumer;
import java.util.List;

public class ConsumerDemo {
    public static void main(String[] args) {
        Consumer<String> print = System.out::println;
        Consumer<String> printUpper = s -> System.out.println(s.toUpperCase());

        print.accept("hello"); // hello

        // andThen – sequential execution of Consumers
        Consumer<String> both = print.andThen(printUpper);
        both.accept("world"); // prints "world" then "WORLD"

        // Real use case: forEach on a collection
        List<String> names = List.of("Alice", "Bob", "Charlie");
        names.forEach(name -> System.out.println("Hello, " + name));
        names.forEach(System.out::println);
    }
}

4. Supplier – Providing Data

import java.util.function.Supplier;
import java.util.Random;

public class SupplierDemo {
    public static void main(String[] args) {
        // No arguments, lazy value provider
        Supplier<String> greeting = () -> "Hello, World!";
        System.out.println(greeting.get()); // Hello, World!

        // Random integer supplier
        Supplier<Integer> randomInt = () -> new Random().nextInt(100);
        System.out.println(randomInt.get()); // e.g., 42

        // Object factory using method reference
        Supplier<java.util.ArrayList<String>> listFactory = java.util.ArrayList::new;
        java.util.ArrayList<String> list = listFactory.get();
        list.add("Java");
        System.out.println(list); // [Java]

        // Lazy fallback with Optional.orElseGet
        String value = java.util.Optional.<String>empty()
            .orElseGet(() -> "default value (lazy)");
        System.out.println(value);
    }
}

5. Comparator – Multi‑Field Sorting

import java.util.*;

public class ComparatorDemo {
    record Person(String name, int age, double salary) {}

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>(List.of(
            new Person("Charlie", 30, 15000),
            new Person("Alice",   25, 20000),
            new Person("Bob",     25, 18000)
        ));

        // Sort by age ascending
        people.sort(Comparator.comparingInt(Person::age));
        people.forEach(p -> System.out.println(p.name() + " " + p.age()));

        // Sort by age ascending, then salary descending when ages are equal
        people.sort(
            Comparator.comparingInt(Person::age)
                .thenComparing(Comparator.comparingDouble(Person::salary).reversed())
        );

        people.forEach(p -> System.out.printf("%s age=%d salary=%.0f%n",
            p.name(), p.age(), p.salary()));
    }
}

6. Defining a Custom Functional Interface

// Optional annotation; recommended for documentation
@FunctionalInterface
public interface StringProcessor {
    String process(String input);

    // Default method does not affect functional‑interface status
    default StringProcessor andThen(StringProcessor after) {
        return input -> after.process(this.process(input));
    }
}

// Usage example
StringProcessor trim = String::trim;
StringProcessor upper = String::toUpperCase;
StringProcessor pipeline = trim.andThen(upper);
System.out.println(pipeline.process("  hello  ")); // HELLO

7. Common Pitfalls

Lambda vs. method reference – s -> s.toUpperCase() is equivalent to String::toUpperCase; the method reference is more concise.

Predicate composition – methods and, or, negate return a new Predicate; the original instance remains unchanged.

Consumer andThen – executes both Consumers sequentially; there is no short‑circuiting.

Functional‑interface type inference – the target type is inferred from the surrounding context, allowing the same lambda signature to be assigned to different functional interfaces.

8. Summary of Functional Interfaces

Predicate  → test()   – condition, combinable with and/or/negate
Function   → apply()  – data transformation, chainable with andThen/compose
Consumer   → accept() – consumes data, commonly used with forEach
Supplier   → get()    – provides data, useful for lazy loading via orElseGet
Comparator → sort()   – multi‑field ordering with comparingXxx + thenComparing
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaLambdaConsumerFunctional InterfaceFunctionComparatorPredicateSupplier
CodeNotes
Written by

CodeNotes

Discuss code and AI, and document daily life and personal growth.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.