Phase 04 — Generics, Streams, and the Functional Edge of Modern Java¶
Why This Phase Exists¶
Every mid-level Java engineer thinks they know generics, streams, and Optional. Most know them well enough to compile, not well enough to reason. This is the phase where study partners separate seniors from mid-levels. You will meet bounded wildcards in a real signature, be asked why List<Object> is not List<String>, and be handed an imperative loop to convert to streams — with the study partner watching to see if you write parallel() for a 10-element list.
Phase 03 taught you shapes (records, sealed, patterns). This phase teaches you pipelines — how values flow through functions with type-safety intact. It also teaches you when the functional style hurts and imperative wins. Both matter.
Weekly Schedule (Months 5–6, roughly 8 weeks)¶
Week |
Focus |
Key file |
|---|---|---|
1 |
Type erasure, bounded types, wildcards |
|
2 |
PECS + generic methods + |
|
3 |
Functional interfaces + lambda capture |
|
4 |
Method references + composition |
|
5 |
Stream anatomy + common mistakes |
|
6 |
Collectors + parallel reality |
|
7 |
Optional done right |
|
8 |
CompletableFuture + virtual threads bridge |
|
Interleave projects.md throughout — the log-analyzer rewrite starts in week 5, the DSL in week 6, the katas from day one.
Exit Criteria¶
You are done with Phase 04 when you can:
Read and write bounded wildcards (
? extends T,? super T) without hesitation, and explain PECS in one sentenceExplain type erasure and its three practical consequences (no
new T[], noT.class, no reified type at runtime)Write a stream pipeline that a reviewer says “reads like intent, not mechanics”
Refuse
parallel()in a code review with a concrete reasonUse
Optionalonly as a return type, and prefermap/filter/orElseThrowoverisPresent/getCompose a 3-stage
CompletableFuturepipeline with proper exception handlingState one situation where virtual threads (Java 21) replace
CompletableFuturechains entirelyShip the three projects in
projects.md
File Map¶
File |
Topic |
Weight |
|---|---|---|
Type erasure, bounds, wildcards, PECS, generic methods |
Heavy |
|
|
Medium |
|
Pipeline anatomy, laziness, collectors, |
Heavy |
|
Return-type-only rule, safe idioms, weaker than Result |
Medium |
|
|
Medium |
|
Log analyzer rewrite, type-safe DSL, 30 stream katas |
Ship these |
Books and References¶
Effective Java, 3rd ed (Bloch, 2018) — Items 26–33 (generics), 42–48 (lambdas and streams), 55 (Optional). Still the reference in 2026 despite the 8-year gap; the Reddit and Coderanch consensus is that no book has replaced it.
Java Concurrency in Practice (Goetz et al, 2006) — dated on APIs but the mental models under
CompletableFutureand virtual threads still trace back here.Modern Java in Action (Urma, Fusco, Mycroft — Manning, 2019) — the streams/functional deep dive Bloch does not fully cover.
Angelika Langer’s Java generics FAQ (free online) — the definitive reference on bounded wildcards, capture conversion, and every generics edge case. Bookmark it.
What Most People Get Wrong¶
Optionalas a field type. It is a return type. Serializers hate it. It adds 16 bytes per field. Bloch is explicit (Item 55).parallel()sprinkled on every stream. DZone’s classic benchmark (Angelika Langer) shows parallel streams can be 15× slower for some workloads. NestedparallelStream()inside another parallel stream shares the sameForkJoinPool.commonPool()— a known anti-pattern.Collectors.toList()in Java 16+ code. Since Java 16,stream.toList()gives you an unmodifiable list in one call. Use it unless you specifically need a modifiableArrayList.Raw types “just to shut up the compiler.”
List raw = new ArrayList()disables all generic checking on that variable — the warnings you suppressed were the compiler protecting you.Lambdas that need three-line comments. If a lambda body exceeds five lines, extract a private method with a name. The functional style is not a license for cleverness.
study Signals¶
study partners listening for seniority will ask:
“What does
List<? extends Number>let you do thatList<Number>does not?” → covariance“When does
List<String>refuse to be aList<Object>?” → invariance and safety“You have a stream over 10 million records — parallel or sequential?” → “It depends; here is how I’d measure.”
“Return type:
UserorOptional<User>or@Nullable User?” → tradeoffs, team convention, serialization impact“CompletableFuture vs virtual threads for this HTTP fanout?” → virtual threads if the code is naturally imperative; CF if you already have a functional pipeline
Answer each of these confidently by phase end.
Return to ../README.md · Previous phase: ../03_oop_design_patterns_modern_java/README.md · Next: 01_generics_deep.md