Lambda Expressions
IntermediateWrite concise, inline function objects with lambda syntax — the foundation of functional-style Java introduced in Java 8.
Overview
A lambda expression is a short anonymous function that can be passed around as a value. It provides a cleaner syntax for implementing functional interfaces compared to anonymous inner classes. The syntax is (parameters) -> body. Lambdas can capture effectively final local variables from the enclosing scope, enabling powerful patterns like callbacks, event handlers, and strategy injection.
Lambda Syntax & Variations
Lambda syntax: (params) -> expression or (params) -> { statements; }
The parameter types can be omitted — the compiler infers them from the functional interface context. Parentheses can be dropped for a single parameter. The return keyword and braces are optional for a single expression.
import java.util.*;
import java.util.function.*;
public class LambdaSyntax {
public static void main(String[] args) {
// Zero parameters
Runnable r = () -> System.out.println("Running");
r.run();
// One parameter — parentheses optional
Consumer<String> print = s -> System.out.println(s.toUpperCase());
print.accept("hello"); // HELLO
// Two parameters
Comparator<Integer> cmp = (a, b) -> a - b;
System.out.println(cmp.compare(3, 5)); // -2
// Block body — multiple statements, explicit return
Function<Integer, String> grade = score -> {
if (score >= 90) return "A";
if (score >= 80) return "B";
return "C";
};
System.out.println(grade.apply(85)); // B
// Lambdas as arguments
List<String> names = new ArrayList<>(List.of("Charlie", "Alice", "Bob"));
names.sort((a, b) -> a.compareTo(b));
System.out.println(names); // [Alice, Bob, Charlie]
names.forEach(name -> System.out.print(name + " "));
System.out.println();
}
}Variable Capture & Effectively Final
Lambdas can capture local variables from the enclosing scope, but those variables must be final or effectively final (never reassigned). Instance fields and static fields can be read and written freely — only local variables are restricted.
Why? Each lambda invocation shares the same captured variable. If it could change between capture and use, behaviour would be unpredictable in concurrent contexts.
import java.util.List;
import java.util.function.Predicate;
public class LambdaCapture {
private int instanceField = 10; // can be freely accessed/modified
void demo() {
int localVar = 5; // effectively final — never reassigned
// localVar = 6; // uncommenting this breaks the lambda below
Predicate<Integer> greaterThanLocal = n -> n > localVar; // OK
Predicate<Integer> greaterThanField = n -> n > instanceField; // OK
System.out.println(greaterThanLocal.test(8)); // true
instanceField = 20; // modifying instance field is fine
System.out.println(greaterThanField.test(15)); // false (20 > 15 = false)
// Capturing loop variable — must copy to effectively final
List<Runnable> tasks = new java.util.ArrayList<>();
for (int i = 0; i < 3; i++) {
final int copy = i; // effectively final copy
tasks.add(() -> System.out.print(copy + " "));
}
tasks.forEach(Runnable::run); // 0 1 2
System.out.println();
}
public static void main(String[] args) { new LambdaCapture().demo(); }
}Lambda vs Anonymous Class
Lambdas replace single-method anonymous inner classes. Key differences: • Lambda has no this — inside a lambda, this refers to the enclosing class • Anonymous class creates a new .class file; lambda is a invokedynamic call site — lighter at runtime • Lambda can only implement a functional interface (one abstract method) • Anonymous class can implement any interface or extend a class
import java.util.Comparator;
public class LambdaVsAnon {
private String name = "Outer";
void compare() {
// Anonymous class — verbose, own 'this'
Comparator<String> anonCmp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
System.out.println(this.getClass().getSimpleName()); // anonymous class name
return a.length() - b.length();
}
};
// Lambda — concise, this = enclosing LambdaVsAnon instance
Comparator<String> lambdaCmp = (a, b) -> {
System.out.println(this.name); // "Outer" — enclosing class this
return a.length() - b.length();
};
System.out.println(anonCmp.compare("hi", "hello")); // -3
System.out.println(lambdaCmp.compare("hi", "hello")); // -3
}
public static void main(String[] args) { new LambdaVsAnon().compare(); }
}Interactive Visualization
stream.filter(n → n%2==0).map(n → n*n).sorted().collect(toList())Key Points to Remember
- Lambda syntax: (params) -> expr or (params) -> { body; return val; }
- Parameter types and parentheses (single param) can be omitted — compiler infers them
- Captured local variables must be final or effectively final
- Inside a lambda, this refers to the enclosing class, not the lambda itself
- Lambdas are compiled to invokedynamic — lighter than anonymous inner classes
- A lambda can only implement a functional interface (exactly one abstract method)
Practice Lambda Expressions in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat is a lambda expression in Java? When was it introduced?
Why must captured local variables be effectively final in lambdas?
What is the difference between a lambda and an anonymous inner class?
Can a lambda expression throw a checked exception?
What does "this" refer to inside a lambda expression?
Ask Aria about Lambda Expressions
Your personal AI tutor — ask anything about this concept