Skip to content

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.

java
@FunctionalInterface
public interface EventHandler {
 void handle(Event event);
}

Lambda Syntax

java
// 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 }

FormExample
No params() -> System.out.println("Hi")
One parammsg -> 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

InterfaceMethodPurpose
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

java
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:

java
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:

java
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

  1. Given a list of strings, use Stream to filter (length > 3), uppercase, sort, and print
  2. Convert an anonymous inner class to Lambda: Runnable r = new Runnable() { @Override public void run() { ... } };
  3. 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.

Built with VitePress | Software Systems Atlas