Lambda Expressions & Functional Interfaces Masterclass

โ˜• Java 21 LTS ๐ŸŸข Lesson 49 ๐Ÿ“‚ Phase 18: Lambda Expressions & Functional Interfaces ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this in-depth guide: Functional Programming Basics ยท Lambda Expression Syntax ยท @FunctionalInterface ยท Predicate ยท Consumer ยท Function ยท Supplier ยท BiFunction ยท Method References (::) ยท Constructor References (::new) ยท Collections forEach & Sorting ยท Variable Capture (Effectively Final Rule)

Before Java 8, Java was strictly object-oriented โ€” to pass behavior (code) into a method, developers had to write bulky, verbose Anonymous Inner Classes. The introduction of Lambda Expressions and Functional Interfaces in Java 8 revolutionized the language, introducing Functional Programming (FP) capabilities. Lambdas allow you to treat functionality as a method argument, write clean declarative code pipelines, and power the Stream API. In this comprehensive masterclass guide, you will master functional interfaces (Predicate, Consumer, Function, Supplier, BiFunction), method references (::), collection iterations, comparator sorting pipelines, and the golden effectively final variable capture rule.

1Functional Programming Basics & Lambda Syntax Anatomy

Functional Programming (FP) ante functions ni First-Class Citizens ga treat cheyyadam โ€” ante functions ni arguments ga pass cheyyavachu, return cheyyavachu, mariyu variables lo store cheyyavachu. Java lo Lambda Expression ante Anonymous Function (peru leni function):

Lambda Syntax Anatomy:
(parameter1, parameter2) -> { body / expression }
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ โ”‚ โ”‚
Parameters Arrow Token Execution Logic

Examples:
1. Zero Params: () -> System.out.println("Hello");
2. Single Param: name -> System.out.println(name); (Parentheses optional!)
3. Multiple: (a, b) -> a + b; (Implicit return!)
4. Multi-line: (a, b) -> { int sum = a + b; return sum * 2; };

๐Ÿ’ก Imperative vs Functional Style (Anonymous Class vs Lambda)

Old Java (Anonymous Inner Class):
Runnable r = new Runnable() { public void run() { System.out.println("Run"); } }; (5 lines of boilerplate!)
Modern Java (Lambda Expression):
Runnable r = () -> System.out.println("Run"); (Clean 1-liner!)

Java 21 โ€” User Curriculum Example
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> names = List.of("Ravi", "Anu", "Kiran");

        // Lambda Expression passed to forEach (Consumer)
        names.forEach(name -> System.out.println(name));
    }
}
2What is a Functional Interface? (@FunctionalInterface Annotation)

A Functional Interface is an interface that contains EXACTLY ONE Single Abstract Method (SAM). It can have any number of default or static methods with concrete implementation, but only 1 abstract method. Lambda expressions can ONLY be assigned to Functional Interfaces:

Java 21 โ€” Custom Functional Interface
@FunctionalInterface
interface MathOperation {
    int operate(int a, int b); // The Single Abstract Method (SAM)

    // Default methods are allowed!
    default void printInfo() {
        System.out.println("Executing math operation...");
    }
}

public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        // Lambda implementing the SAM
        MathOperation addition = (a, b) -> a + b;
        MathOperation multiplication = (a, b) -> a * b;

        System.out.println("10 + 20 = " + addition.operate(10, 20)); // 30
        System.out.println("10 * 20 = " + multiplication.operate(10, 20)); // 200
    }
}
3The Core 5 Functional Interfaces in java.util.function โญ (Must-Know)

Java standard library java.util.function package lo enterprise applications ki avasaramaina 5 core functional interfaces provide chesindi:

InterfaceAbstract MethodInput $ ightarrow$ OutputKey Purpose & Real-World Use
Predicate<T> boolean test(T t) T $ ightarrow$ boolean Condition checking, filtering items in Streams (e.g. user.getAge() >= 18).
Consumer<T> void accept(T t) T $ ightarrow$ void Consuming data / side-effects (e.g. System.out.println, sending emails).
Function<T, R> R apply(T t) T $ ightarrow$ R Data transformation & mapping (e.g. converting String to Integer length).
Supplier<T> T get() none $ ightarrow$ T Factory generator, lazy loading, creating UUIDs, generating timestamps.
BiFunction<T, U, R> R apply(T t, U u) (T, U) $ ightarrow$ R Taking 2 arguments of different types and returning a computed result R.
Java 21 โ€” All 5 Core Functional Interfaces in Action
import java.util.function.*;

public class CoreInterfacesDemo {
    public static void main(String[] args) {
        // 1. Predicate: Check if number is even
        Predicate<Integer> isEven = n -> n % 2 == 0;
        System.out.println("Is 14 even? " + isEven.test(14)); // true

        // 2. Consumer: Print formatted string
        Consumer<String> greeter = name -> System.out.println("Namaste, " + name + "!");
        greeter.accept("Ravi"); // Namaste, Ravi!

        // 3. Function: Convert string to character length
        Function<String, Integer> stringLength = str -> str.length();
        System.out.println("Length of 'Java 21': " + stringLength.apply("Java 21")); // 7

        // 4. Supplier: Provide current timestamp
        Supplier<Double> randomSupplier = () -> Math.random();
        System.out.println("Random Value: " + randomSupplier.get());

        // 5. BiFunction: Combine Name and Salary into Employee Record String
        BiFunction<String, Double, String> empFormatter = 
            (name, salary) -> "Employee: " + name + " | Salary: Rs." + salary;
        System.out.println(empFormatter.apply("Sneha", 95000.0));
    }
}
4Method References (::) & Constructor References (::new)

Lambda expression kevalam existing method ni direct ga invoke chesthunte, dhaanni inka compact ga rayadaniki Method Reference (::) vadathamu:

Method Reference TypeLambda SyntaxMethod Reference Shorthand (::)
1. Static Methodstr -> Integer.parseInt(str)Integer::parseInt
2. Instance Method of Specific Objectx -> System.out.println(x)System.out::println
3. Instance Method of Arbitrary Objectstr -> str.toUpperCase()String::toUpperCase
4. Constructor Reference() -> new ArrayList<>()ArrayList::new
Java 21 โ€” Method References Demo
import java.util.List;
import java.util.ArrayList;
import java.util.function.Function;
import java.util.function.Supplier;

public class MethodRefDemo {
    public static void main(String[] args) {
        List<String> cities = List.of("hyderabad", "bengaluru", "chennai");

        // 1. Instance Method Reference (String::toUpperCase)
        cities.stream().map(String::toUpperCase).forEach(System.out::println);

        // 2. Static Method Reference (Integer::parseInt)
        Function<String, Integer> parser = Integer::parseInt;
        System.out.println("Parsed: " + (parser.apply("500") + 100)); // 600

        // 3. Constructor Reference (ArrayList::new)
        Supplier<List<String>> listFactory = ArrayList::new;
        List<String> dynamicList = listFactory.get();
        dynamicList.add("Spring Boot");
        System.out.println("Created List: " + dynamicList);
    }
}
5Lambdas with Collections & Multi-Field Sorting (Comparator)

Java 8+ Collections lo forEach, removeIf, replaceAll, mariyu sort methods Lambdas tho integration ayyi unnay:

Java 21 โ€” Collection Sorting with Comparator Pipelines
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class Developer {
    private String name;
    private int experienceYears;
    private double salary;

    public Developer(String name, int exp, double salary) {
        this.name = name;
        this.experienceYears = exp;
        this.salary = salary;
    }

    public String getName() { return name; }
    public int getExperienceYears() { return experienceYears; }
    public double getSalary() { return salary; }

    @Override
    public String toString() {
        return name + " (" + experienceYears + " yrs) - Rs." + salary;
    }
}

public class SortingDemo {
    public static void main(String[] args) {
        List<Developer> team = new ArrayList<>();
        team.add(new Developer("Ravi", 4, 75000));
        team.add(new Developer("Anu", 7, 120000));
        team.add(new Developer("Kiran", 2, 45000));
        team.add(new Developer("Bhavna", 7, 135000));

        // 1. removeIf with Predicate: Remove developers with < 3 yrs experience
        team.removeIf(dev -> dev.getExperienceYears() < 3);

        // 2. Multi-Level Sorting: By Experience DESC, then by Salary DESC
        team.sort(
            Comparator.comparingInt(Developer::getExperienceYears).reversed()
                      .thenComparingDouble(Developer::getSalary).reversed()
        );

        System.out.println("=== Sorted Senior Developers ===");
        team.forEach(System.out::println);
    }
}
6Variable Capture & The "Effectively Final" Rule โš ๏ธ

โš ๏ธ Critical Rule: Why Captured Variables Must Be Final

Lambda expression lopala outer method lo unna local variables ni read cheyyavachu (called Variable Capture). Kaani aa variable <code>final</code> or <code>effectively final</code> (declare chesina tharvatha reassign cheyyakunda undali)!
Why? Local variable Stack Memory lo untundhi. Outer method execute aypoyaka stack frame destroy avthundhi, kaani Lambda Object Heap lo untundhi. So Java local variable copy ni capture chesthundhi. Synchronization conflicts avoid cheyyadaniki Java reassignments ni prohibit chesthundhi!

Java 21 โ€” Variable Capture Demo
public class VariableCaptureDemo {
    public static void main(String[] args) {
        String companyPrefix = "OUR_COMPILER_"; // Effectively final variable

        List<String> roles = List.of("DEV", "TESTER", "ARCHITECT");

        // โœ… Allowed: Reading effectively final variable
        roles.forEach(role -> {
            System.out.println(companyPrefix + role);
        });

        // โŒ If you try to reassign:
        // companyPrefix = "NEW_PREFIX_"; 
        // Compilation Error: Variable used in lambda expression should be final or effectively final!
    }
}
๐Ÿ’ป Try It Yourself โ€” Test in Live Java 21 Compiler

Run this complete functional lambda pipeline in our online Java compiler:

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

public class Main {
    public static void main(String[] args) {
        List<String> languages = List.of("Java", "JavaScript", "Python", "C++", "Julia");

        Predicate<String> startsWithJ = lang -> lang.startsWith("J");

        languages.stream()
                 .filter(startsWithJ)
                 .map(String::toUpperCase)
                 .forEach(System.out::println);
    }
}
Open in Online Java Compiler โ†’
OC
Written and reviewed by Our Compiler Technical Team ยท Updated for Java 21 LTS (2026 Edition)