Writing Simple Lambdas
OCP Exam Topic — Write and use lambda expressions. Covered in Chapter 8 of OCP Java SE 21 Developer Study Guide (Selikoff & Boyarsky).
What Is a Lambda?
A lambda expression is an anonymous block of code that can be passed around and executed later. Think of it as a concise way to implement a functional interface — an interface with exactly one abstract method — without writing a full anonymous class.
java// Anonymous class (verbose)
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Running");
}
};
// Lambda (concise)
Runnable r2 = () -> System.out.println("Running");Both r1 and r2 are valid Runnable implementations. The lambda on the right is the preferred modern style.
Lambda Syntax
The general form is:
plaintext(parameters) -> body
| Part | Description |
|---|---|
(parameters) |
Zero or more parameters; parentheses required when zero or two+ parameters |
-> |
Arrow token (required) |
body |
A single expression or a block { } |
Single-Expression Body
When the body is a single expression, no braces or return statement are needed — the expression value is returned automatically.
java// No parameters
Runnable greet = () -> System.out.println("Hello");
// One parameter — parentheses optional for single param
Comparator<String> byLength = (a, b) -> a.length() - b.length();Multi-Statement Body
When you need more than one statement, wrap the body in braces. A return statement is required if the abstract method has a non-void return type.
javaComparator<String> detailed = (a, b) -> {
int lengthDiff = a.length() - b.length();
if (lengthDiff != 0) return lengthDiff;
return a.compareTo(b);
};Type Inference
Java infers parameter types from the target functional interface. You can omit them when the context is clear.
java// Explicit types
Comparator<String> c1 = (String a, String b) -> a.compareTo(b);
// Inferred types (preferred)
Comparator<String> c2 = (a, b) -> a.compareTo(b);You must either declare types for all parameters or omit them for all parameters — you cannot mix.
java// Does NOT compile — mixing declared and inferred
Comparator<String> bad = (String a, b) -> a.compareTo(b);Omitting Parentheses
Parentheses around a single parameter may be omitted only when the type is inferred:
java// Both are valid
java.util.function.Predicate<String> p1 = s -> s.isEmpty();
java.util.function.Predicate<String> p2 = (s) -> s.isEmpty();
// Explicit type requires parentheses
java.util.function.Predicate<String> p3 = (String s) -> s.isEmpty();Accessing Variables
Lambdas can read variables from the enclosing scope, but those variables must be effectively final — assigned exactly once and never reassigned.
javaString prefix = "Hello"; // effectively final
java.util.function.Consumer<String> greet = name -> System.out.println(prefix + ", " + name);
greet.accept("Alice"); // prints "Hello, Alice"Attempting to modify prefix after it is captured causes a compile error:
javaString prefix = "Hello";
prefix = "Hi"; // prefix is no longer effectively final
// Does NOT compile
java.util.function.Consumer<String> greet = name -> System.out.println(prefix + ", " + name);Instance variables and static variables do not need to be effectively final — they can be read and written freely inside a lambda.
Lambda as a Functional Interface Implementation
A lambda expression is always the implementation of exactly one abstract method. The target type determines which functional interface is used.
javainterface Transformer {
String transform(String input);
}
Transformer upper = s -> s.toUpperCase();
System.out.println(upper.transform("java")); // "JAVA"The same lambda body can satisfy different functional interfaces as long as the abstract method signatures are compatible:
javajava.util.function.Function<String, String> fn = s -> s.toUpperCase();
java.util.function.UnaryOperator<String> op = s -> s.toUpperCase();
Transformer tr = s -> s.toUpperCase();All three variables hold equivalent behaviour.
Key Points to Remember
- Lambda syntax:
(params) -> bodyor(params) -> { statements; }. - The arrow
->is always required. - Single-expression bodies return implicitly; block bodies require an explicit
return. - Parameter types are inferred from the target functional interface; omit or include them consistently.
- Parentheses around a single inferred parameter are optional.
- Captured local variables must be effectively final.
- A lambda is always the implementation of a functional interface's single abstract method.