patternjavaspringTip
Sealed classes enforce exhaustive pattern matching in switch expressions
Viewed 0 times
Java 17+ (sealed), Java 21+ (exhaustive switch)
sealed classespattern matchingswitch expressionexhaustivediscriminated unionJava 17Java 21
Problem
Class hierarchies used as discriminated unions have no compiler enforcement: a switch on a base type with instanceof checks compiles even when new subtypes are added without updating the switch, leading to silent runtime misses.
Solution
Use sealed classes to declare all permitted subtypes and combine with exhaustive switch expressions (Java 21+):
Adding a new permitted type causes a compile error in all exhaustive switches, forcing every call site to be updated.
// Define the sealed hierarchy
public sealed interface PaymentResult
permits PaymentResult.Success, PaymentResult.Failure, PaymentResult.Pending {}
public record Success(String transactionId, BigDecimal amount) implements PaymentResult {}
public record Failure(String errorCode, String message) implements PaymentResult {}
public record Pending(String pendingId, Instant estimatedCompletion) implements PaymentResult {}
// Exhaustive switch — compiler error if a permitted type is missing
public String describeResult(PaymentResult result) {
return switch (result) {
case Success s -> "Paid " + s.amount() + ", txn=" + s.transactionId();
case Failure f -> "Failed: " + f.message();
case Pending p -> "Pending until " + p.estimatedCompletion();
};
}Adding a new permitted type causes a compile error in all exhaustive switches, forcing every call site to be updated.
Why
The Java compiler tracks all permitted subtypes of a sealed hierarchy. A switch expression without a default that covers all permitted subtypes is exhaustive, and the compiler verifies this at compile time.
Gotchas
- Sealed classes require all permitted subtypes to be in the same compilation unit (same package/module)
- Switch expressions require Java 14+ (preview) or Java 17+ (stable); exhaustive sealed switches require Java 21+
- Adding a default arm to the switch defeats the exhaustiveness check — remove it when using sealed hierarchies
Revisions (0)
No revisions yet.