Pattern Matching for instanceof
Use modern Java pattern matching with instanceof, guarded patterns, and record deconstruction.
import java.util.*;
public class PatternMatchingDemo {
// Sealed hierarchy
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
// Pattern matching with instanceof
static double area(Shape shape) {
if (shape instanceof Circle c) {
return Math.PI * c.radius() * c.radius();
} else if (shape instanceof Rectangle r) {
return r.width() * r.height();
} else if (shape instanceof Triangle t) {
return 0.5 * t.base() * t.height();
}
throw new IllegalArgumentException("Unknown shape");
}
// Switch pattern matching (Java 21+)
static String describe(Shape shape) {
return switch (shape) {
case Circle c when c.radius() > 10 -> "Large circle (r=" + c.radius() + ")";
case Circle c -> "Circle (r=" + c.radius() + ")";
case Rectangle r -> "Rectangle (" + r.width() + "x" + r.height() + ")";
case Triangle t -> "Triangle (base=" + t.base() + ")";
};
}
// Pattern matching with null
static String format(Object obj) {
return switch (obj) {
case null -> "null";
case Integer i -> "int: " + i;
case String s -> "str: \"" + s + "\"";
case List<?> list -> "list[" + list.size() + "]";
case int[] arr -> "array[" + arr.length + "]";
default -> obj.getClass().getSimpleName() + ": " + obj;
};
}
// Real-world: JSON-like processing
sealed interface JsonValue permits JsonString, JsonNumber, JsonArray, JsonObject {}
record JsonString(String value) implements JsonValue {}
record JsonNumber(double value) implements JsonValue {}
record JsonArray(List<JsonValue> elements) implements JsonValue {}
record JsonObject(Map<String, JsonValue> fields) implements JsonValue {}
static String stringify(JsonValue value) {
return switch (value) {
case JsonString s -> "\"" + s.value() + "\"";
case JsonNumber n -> String.valueOf(n.value());
case JsonArray a -> "[" + a.elements().stream()
.map(PatternMatchingDemo::stringify)
.reduce((x, y) -> x + "," + y)
.orElse("") + "]";
case JsonObject o -> "{" + o.fields().entrySet().stream()
.map(e -> "\"" + e.getKey() + "\":" + stringify(e.getValue()))
.reduce((x, y) -> x + "," + y)
.orElse("") + "}";
};
}
public static void main(String[] args) {
List<Shape> shapes = List.of(
new Circle(5), new Rectangle(3, 4), new Triangle(6, 8));
shapes.forEach(s -> System.out.printf("%s → area=%.2f%n", describe(s), area(s)));
System.out.println(format(42));
System.out.println(format("hello"));
System.out.println(format(null));
}
}Use Cases
- Type-safe polymorphic processing
- Exhaustive switch over sealed types
- ADT-style data modeling in Java
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
Sealed Classes and Pattern Matching
Use sealed interfaces with pattern matching switch for type-safe, exhaustive algebraic data types.
Best for: Type-safe state machines and event handling
Switch Expressions — Modern Java
Use switch expressions with arrow syntax, pattern matching, guards, and yield for concise branching.
Best for: Concise branching logic replacing verbose if-else
Java Records — Immutable Data Classes
Use Java records for concise immutable data carriers with built-in equals, hashCode, and toString.
Best for: Replacing verbose POJO classes with concise records
Text Blocks — Multi-line Strings
Use Java text blocks for readable multi-line strings: SQL, JSON, HTML, and formatted templates.
Best for: Embedding SQL queries in Java code