Modern Java 17-21
Sealed classes, pattern matching for instanceof and switch, record patterns, text blocks, virtual threads and more.
Java has changed more since Java 8 than in its first two decades. A new feature release ships every six months, and every two years one becomes a Long-Term Support (LTS) release: 17 (2021), 21 (2023) and 25 (2025). This lesson tours the features from Java 17 to 21 that change how you write everyday code. Many of them work together: records + sealed types + pattern matching add up to a new, data-oriented style of programming in Java.
Quick recap of features you've already used#
Sealed classes and interfaces (Java 17)#
Normally anyone can extend a class or implement an interface. A sealed type restricts which classes may extend it:
Rules:
permitslists the allowed direct subtypes. If they're in the same file,permitscan be omitted.- Every permitted subtype must be
final,sealed(with its own list) ornon-sealed(open again for extension). - Permitted classes must be in the same package (or the same module).
Why it matters: the compiler now knows every possible case. A switch over a sealed type is exhaustive without default, and if you later add record Wallet(...) implements Payment, every switch that forgot it becomes a compile error, not a runtime surprise. Sealed types model "one of a fixed set of shapes": payment methods, API results, AST nodes, events.
Pattern matching for instanceof (Java 16)#
The pattern variable s is in scope wherever the compiler knows the test succeeded, including after && and in the rest of a method after an early return such as if (!(obj instanceof String s)) return;.
Pattern matching for switch (Java 21)#
switch can now match on types, with guards, and handle null:
case Type name ->matches by type and binds a variable.whenadds a guard condition.- Cases are checked top to bottom. The compiler rejects a case that can never match because an earlier one dominates it (e.g.
case Integer ibeforecase Integer i when i < 0). case nullis allowed. Without it, anullselector throwsNullPointerException, as before.- Over non-sealed types like
Object, adefaultis required for exhaustiveness.
Record patterns (Java 21)#
Record patterns deconstruct a record into its components, and they nest:
You can use var for component types, and from Java 22 the unnamed pattern _ for components you don't need: case Circle(_, int r).
Data-oriented programming: putting it together#
Sealed interfaces + records + pattern-matching switches give you algebraic data types: a precise, compiler-checked model of your domain's possibilities. A typical example is modelling the result of an operation without exceptions:
Compare this with the classic OOP approach of putting a method on each subclass. Both are valid. Pattern matching is best when the set of types is fixed but you keep adding operations (reports, validations, conversions); virtual methods are best when the operations are fixed but you keep adding types.
Text blocks (Java 15) in more depth#
- Indentation is stripped based on the least-indented line (including the closing
"""). \at the end of a line joins it with the next line;\skeeps a space that would otherwise be stripped.- Combine them with
formatted(...)for templates. (String templates were previewed in Java 21-22 and then withdrawn, soformattedremains the way.)
Virtual threads (Java 21)#
Covered in the executors lesson. The essentials:
They let you write simple, blocking, thread-per-request code that scales like asynchronous code for I/O-heavy workloads. In Spring Boot 3.2+, enable them with spring.threads.virtual.enabled=true.
Sequenced collections (Java 21)#
New interfaces SequencedCollection, SequencedSet and SequencedMap give every ordered collection a uniform API:
Other handy additions (Java 11-21)#
Other notable changes: the java.net.http.HttpClient (Java 11) for HTTP calls, running single-file programs with java File.java (Java 11), jpackage for native installers (Java 16), and generational ZGC (Java 21).
A peek beyond Java 21#
Java 25 (LTS, September 2025) finalised several features that build on 21:
- Compact source files and instance main methods: a beginner program can be just
void main() { IO.println("Hello"); }. - Unnamed variables and patterns with
_(final in Java 22). - Flexible constructor bodies: statements before
super(...), e.g. for validation. - Module import declarations:
import module java.base;. - Scoped values for safely sharing data with virtual threads.
Everything in this course runs on Java 21 and on these newer versions.
Common mistakes#
- Forgetting that permitted subclasses of a sealed type must be
final,sealedornon-sealed. - Ordering switch cases so a general pattern comes before a specific one (a compile error: "this case label is dominated by a preceding case label").
- Adding
defaultto a switch over a sealed type. It compiles, but you lose the compile-time warning when a new subtype appears. - Using pattern matching where a simple polymorphic method would be clearer.
- Assuming preview features (
--enable-preview) are stable. Don't use them in production code.
What's next#
You now know modern core Java. The final lesson is your next step into professional back-end development: an introduction to Spring Boot.
Check your understanding
Quick quiz
1.What does
sealed interface Shape permits Circle, Square {}guarantee?2.In Java 21, what does
case Circle c when c.radius() > 10 ->do in a switch?3.Which statement about record patterns is correct?
Finished reading?
Mark this lesson complete to track your progress.