Lambda syntax & effectively-final capture
Writing lambdas in all their forms — and the capture rule that trips up the exam.
Lambdas: code as a value
A lambda is an anonymous function you can pass around as a value. It implements a functional interface — an interface with exactly one abstract method. The compiler figures out which method by looking at the target type.
The three syntax forms:
// 1 — multi-param, expression body
BinaryOperator<Integer> add = (a, b) -> a + b;
// 2 — single param: parentheses optional
Predicate<String> empty = s -> s.isEmpty();
// 3 — block body with an explicit return
Function<Integer, String> describe = n -> {
if (n > 0) return "positive";
if (n < 0) return "negative";
return "zero";
};
Type annotations on parameters are optional when the compiler can infer them from context — and it usually can.
Variable capture: a lambda can read local variables from its enclosing scope, but only if those variables are effectively final — never reassigned after their first assignment. The rule applies to both final variables and variables that *could* have been declared final:
int threshold = 5; // effectively final — never reassigned
Predicate<Integer> big = n -> n > threshold; // OK
int mutable = 5;
mutable = 6; // not effectively final
Predicate<Integer> bad = n -> n > mutable; // COMPILE ERROR
Lambdas can freely access this and instance fields of the enclosing class — only local variables are subject to the effectively-final rule.