Java Functional Interfaces - Real-World Revision Guide

SAM, lambdas, built-in interfaces, and custom domain interfaces.

🧠Core Concepts

  • Functional Interface: Exactly one abstract method (SAM).
  • @FunctionalInterface: Enforces SAM at compile-time and improves readability.
  • Can still have: Multiple default and static methods.
  • Main benefit: Enables passing behavior as lambda expressions.

Why Not Classes?

@FunctionalInterface applies only to interfaces. Classes represent structure/state, while functional interfaces define behavior contracts that lambdas implement dynamically.

📦Example 1: Built-in Interface (Function<T, R>)

Use case: Convert UserEntity (DB model) into UserDTO (API-safe model).

Key method: R apply(T t) — called directly or by Stream.map() internally.

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

class UserEntity {
    private final Long id;
    private final String firstName;
    private final String lastName;
    private final String email;
    private final String passwordHash;

    public UserEntity(Long id, String firstName, String lastName, String email, String passwordHash) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.passwordHash = passwordHash;
    }

    public Long getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
}

class UserDTO {
    private final Long id;
    private final String fullName;
    private final String email;

    public UserDTO(Long id, String fullName, String email) {
        this.id = id;
        this.fullName = fullName;
        this.email = email;
    }

    @Override
    public String toString() {
        return "UserDTO{id=" + id + ", fullName='" + fullName + "', email='" + email + "'}";
    }
}

public class DtoMappingExample {
    private static final Function<UserEntity, UserDTO> convertToDTO = entity ->
        new UserDTO(
            entity.getId(),
            entity.getFirstName() + " " + entity.getLastName(),
            entity.getEmail().toLowerCase()
        );

    public static void main(String[] args) {
        UserEntity singleDbUser = new UserEntity(101L, "Alice", "Wonderland", "ALICE@example.com", "$2a$12$pPlk8..");
        UserDTO singleUserDTO = convertToDTO.apply(singleDbUser);
        System.out.println(singleUserDTO);

        List<UserEntity> databaseUsers = Arrays.asList(
            new UserEntity(1L, "John", "Doe", "John.Doe@example.com", "$2a$12$eImiTx.."),
            new UserEntity(2L, "Jane", "Smith", "JANE.SMITH@example.com", "$2a$12$Kj9xY..")
        );

        List<UserDTO> apiResponse = databaseUsers.stream()
                .map(convertToDTO)
                .collect(Collectors.toList());

        apiResponse.forEach(System.out::println);
    }
}

🧩Example 2: Custom Functional Interface (Domain Logic)

Use case: Credential validation with explicit business intent.

@FunctionalInterface
public interface IdentityValidator {
    boolean validate(String username, String password);
}

public class AuthenticationRegistry {
    public void executeValidation() {
        IdentityValidator prodCheck = (user, pass) ->
            user.startsWith("admin_") && pass.length() > 12;

        boolean result = prodCheck.validate("admin_root", "SecurePassword123!");
        System.out.println("Authentication validation passing status: " + result);
    }

    public static void main(String[] args) {
        new AuthenticationRegistry().executeValidation();
    }
}
Key takeaway: IdentityValidator is clearer and more domain-driven than generic BiPredicate<String, String>.

Quick Revision Checklist

  • SAM = one abstract method only.
  • Prefer @FunctionalInterface for compiler checks.
  • Use built-ins for common transformations/filtering.
  • Create custom interfaces when business meaning matters.
  • Lambdas make code concise, composable, and test-friendly.