library
intermediateLambda Expressions
Write concise function literals and understand how they relate to functional interfaces, closures, and anonymous classes.
A lambda is a short way to implement an interface that has exactly one abstract method.
Lambda = a sticky note with a quick instruction ('add these two numbers'). Anonymous class = a full letter with letterhead, signature, and envelope for the same instruction.
Key Concepts
1
Runnable r = () -> print("hi");
Comparator<String> byLen = (a, b) -> a.length() - b.length();
Function<Integer, Integer> dbl = x -> x * 2;2
The forms.
() -> expr no parameters
x -> expr one parameter, parentheses optional
(a, b) -> expr several
(int a, int b) -> expr with explicit types
(a, b) -> { ... return v; } a block needs an explicit return3
It must be a functional interface — one abstract method. That is what makes the target type unambiguous:
4
@FunctionalInterface
interface Validator { boolean test(String s); }5
Validator notEmpty = s -> !s.isBlank();6
Capture is limited to effectively final locals.
int total = 0;
list.forEach(x -> total += x); // will not compile7
The lambda may outlive the method, so it copies the value rather than sharing the variable. Fields are
different — those it can read and write, because it holds the object.
8
int sum = list.stream().mapToInt(Integer::intValue).sum(); // the fix9
this means the enclosing object, not the lambda. In an anonymous class this meant the anonymous
instance, which is a real behavioural difference when migrating:
this
10
class A {
void f() {
Runnable r = () -> print(this); // the A
Runnable s = new Runnable() {
public void run() { print(this); } // the Runnable
};
}
}11
A lambda is not an anonymous class underneath. The compiler emits an invokedynamic instruction, and
LambdaMetafactory builds the implementation on first use. So there is no extra .class file per lambda,
and a stateless one can be cached and reused.
invokedynamicLambdaMetafactory.class
12
Where a lambda cannot go: extending a class, implementing two methods, or needing a name to refer to
itself recursively. Those still want an anonymous or named class.
13
What the interviewer is probing.1. "Why must a lambda capture only effectively final locals?" Probing: lifetime. Stalls: "It
is a compiler restriction." Moves up: the lambda may outlive the method frame, so the value is
copied; allowing reassignment would make it ambiguous which value was meant.
14
2. "Is a lambda compiled to an anonymous inner class?" Probing: what actually happens.
Stalls: "Yes, syntactic sugar." Moves up: no — the compiler emits invokedynamic and
LambdaMetafactory builds the implementation at first use, so there is no extra class file.
15
3. "How do you accumulate a total inside forEach?" Probing: the capture restriction in
practice. Stalls: "Use a local variable." Moves up: you cannot mutate a local — use a stream
reduction or sum, or an accumulator object if the side effect is genuinely needed.
16
4. "What makes an interface usable as a lambda target?" Probing: the functional interface
rule. Stalls: "The @FunctionalInterface annotation." Moves up: exactly one abstract method — the
annotation only asks the compiler to check, and default and static methods do not count.