Java lambda expressions and functional interfaces
lambda syntax, functional interface, @FunctionalInterface, Predicate, Function, Consumer, Supplier, BiFunction, method references, type inference
Lambda Expressions
Lambdas are concise anonymous functions. They implement functional interfaces — interfaces with exactly one abstract method — inline without declaring a full class.
Syntax Evolution
// Full anonymous class
Comparator c1 = new Comparator() {
@Override
public int compare(String a, String b) { return a.compareTo(b); }
};
// Lambda with explicit types
Comparator c2 = (String a, String b) -> a.compareTo(b);
// Lambda with inferred types (preferred)
Comparator c3 = (a, b) -> a.compareTo(b);
// Method reference (most concise when delegating to existing method)
Comparator c4 = String::compareTo;
Built-in Functional Interfaces
Predicate isEven = n -> n % 2 == 0;
Function len = String::length;
Consumer print = System.out::println;
Supplier> fresh = ArrayList::new;
System.out.println(isEven.test(4)); // true
System.out.println(len.apply("hello")); // 5
print.accept("Lambda!"); // Lambda!
List list = fresh.get();
Lambdas can capture local variables that are effectively final — never reassigned after initialization. A variable does not need the final keyword; it just must not be modified. Trying to capture a mutated variable is a compile error.
Use method references when the lambda body only calls a single existing method that matches the functional interface's signature. They reduce noise and make the intent immediately clear.
Compose predicates using and(), or(), and negate() built into Predicate: isEven.and(n -> n > 10) returns a new Predicate that checks both conditions. This avoids nested lambdas and keeps each condition independently testable.
