Sections in this lesson

Modern Java Roadmap

Connect Java 8 fundamentals to records, sealed types, pattern matching and Java 21 concurrency.

Reviewed: · Content owner: Interview Preparator

What you will learn

  • Map features to their minimum stable language version.
  • Model closed result types without preview flags.
  • Separate runtime selection from the compilation target.

Before you start

The flow

  1. 1.Choose baseline
  2. 2.Model data
  3. 3.Handle variants
  4. 4.Compile
  5. 5.Verify

A practical upgrade sequence

Keep the existing Java 8 topics: streams, lambdas and Optional remain useful. Add records (stable in Java 16), sealed types (Java 17), and pattern switch and virtual threads (Java 21) as explicit milestones. Do not label every feature in a new JDK as final.

This curriculum targets Java 21 without preview flags. Running a newer JDK does not automatically make the application compatible with an older runtime: use --release to compile against the chosen Java API and bytecode level.

Make states explicit

The example combines a sealed result family with record values and an exhaustive switch. Adding another permitted result forces the switch to be reconsidered at compilation, rather than hiding an unhandled case behind a default branch.

Use closed hierarchies when the variants are controlled together. Plugin ecosystems with unknown third-party extensions may need open interfaces instead.

Upgrade the system, not only syntax

Check framework, build plugin, agent and library support; compare startup, GC behavior and representative workloads. Keep a rollback artifact. Features such as structured concurrency must be checked against the exact JDK release and preview policy before becoming a production dependency.

Try it

ModernJava.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.List;
public class ModernJava {
sealed interface Result permits Success, Failure {}
record Success(List<String> topics) implements Result {
Success { topics = List.copyOf(topics); }
}
record Failure(String reason) implements Result {}
static String describe(Result result) {
return switch (result) {
case Success(var topics) -> "Topics: " + topics.size();
case Failure(var reason) -> "Failed: " + reason;
};
}
public static void main(String[] args) {
if (!describe(new Success(List.of("records"))).equals("Topics: 1")) {
throw new AssertionError("Unexpected success branch");
}
if (!describe(new Failure("missing")).equals("Failed: missing")) {
throw new AssertionError("Unexpected failure branch");
}
System.out.println("Java 21: records, sealed types and exhaustive pattern switch.");
}
}

Run npm run examples:java with JDK 21+. Compiled with --release 21, without preview flags.

Interview questions

Why use --release rather than only -target?

It also constrains the exposed platform API, reducing accidental calls to APIs absent from the target runtime.

When is a sealed hierarchy useful?

For a controlled set of domain variants where exhaustive handling is valuable.

Does a newer runtime remove migration work?

No. Dependencies, reflection, build tooling, observability and performance must still be checked.

Practice

Add a Pending result and let the compiler reveal every switch that must change.

Check your solution

  • Every variant has behavior.
  • The example compiles with --release 21 and no preview flags.
  • Runtime and target versions are documented.

Official references

Continue learning

Category sequence

Java

Lesson 40 / 42

Was this lesson useful?

Change notes: Phase 2: bilingual lesson with versioned executable reference.