Metadata Card
- Prerequisites: Ch5 (Methods), Ch8 (Interfaces)
- Estimated time: 40 min
- Core difficulty:
- Reading mode: High focus
- Completion marker: Can write Lambda expressions and method references, use Stream API for collection pipelines
The Breakthrough · Tracing the Origins
Problem: every new event type requires a new class implementing an interface. That's a lot of boilerplate for "when this happens, run this code."
Lambda lets you pass behavior as a value — like passing a number or a string, but passing code.
Functional Interfaces
A functional interface has exactly one abstract method. Think of it as a "socket" for Lambda.
@FunctionalInterface
public interface EventHandler {
void handle(Event event);
}Lambda Syntax
// Anonymous class (old way)
EventHandler handler = new EventHandler() {
@Override
public void handle(Event event) {
System.out.println("Event: " + event.name());
}
};
// Lambda (new way)
EventHandler handler = (event) -> System.out.println("Event: " + event.name());Language: Java 8+ Syntax: (parameters) -> { body }
| Form | Example |
|---|---|
| No params | () -> System.out.println("Hi") |
| One param | msg -> System.out.println(msg) (parens optional) |
| Multiple params | (a, b) -> a - b |
| Multi-line | (a, b) -> { int r = a + b; return r; } |
The Four Core Functional Interfaces
| Interface | Method | Purpose |
|---|---|---|
Supplier<T> | T get() | Produce a value |
Consumer<T> | void accept(T) | Consume a value |
Function<T,R> | R apply(T) | Transform a value |
Predicate<T> | boolean test(T) | Test a condition |
Method References
Consumer<String> printer = System.out::println;
Function<String, Integer> parser = Integer::parseInt;
Function<String, Integer> getLen = String::length;
Supplier<List<String>> listMaker = ArrayList::new;Stream API — Collection Pipelines
Without Stream:
List<String> result = new ArrayList<>();
for (Villager v : villagers) {
if (v.age() > 18) result.add("[Village] " + v.name());
}
Collections.sort(result);
List<String> top5 = result.subList(0, Math.min(5, result.size()));With Stream:
List<String> result = villagers.stream()
.filter(v -> v.age() > 18)
.map(v -> "[Village] " + v.name())
.sorted()
.limit(5)
.collect(Collectors.toList());Intermediate operations (lazy): filter, map, sorted, limitTerminal operations (trigger): collect, forEach, count, reduce
Common Pitfalls: Lambda variables must be effectively final. Stream can only be consumed once. Don't use side effects in map.
Final Challenge
- Given a list of strings, use Stream to filter (length > 3), uppercase, sort, and print
- Convert an anonymous inner class to Lambda:
Runnable r = new Runnable() { @Override public void run() { ... } }; - Convert
s -> Integer.parseInt(s)to a method reference
Traveler's Notes
Lambda packages behavior into an envelope and passes it around.
Functional interface = socket. Lambda = plug.
Method references shorten Lambda one step further.
Stream pipeline = filter → map → sorted → collect.