Pattern Matching
Switch Pattern Matching
When handling multiple object types in event handlers, parsers, or command dispatchers, cascading if-else chains become unwieldy. Switch pattern matching lets you dispatch on types elegantly, with automatic casting and compiler-verified exhaustiveness.
Basic Type Patterns
Basic.java
public class Basic {
public static void main(String[] args) {
Object obj = ;
switch (obj) {
case String s -> System.out.println("String: " + s.toUpperCase());
case Integer i -> System.out.println("Integer: " + i * 2);
case Double d -> System.out.println("Double: " + d / 2);
default -> System.out.println("Unknown type");
}
}
}
public class Basic {
public static void main(String[] args) {
Object obj = ;
switch (obj) {
case String s -> System.out.println("String: " + s.toUpperCase());
case Integer i -> System.out.println("Integer: " + i * 2);
case Double d -> System.out.println("Double: " + d / 2);
default -> System.out.println("Unknown type");
}
}
}
public class Basic {
public static void main(String[] args) {
Object obj = ;
switch (obj) {
case String s -> System.out.println("String: " + s.toUpperCase());
case Integer i -> System.out.println("Integer: " + i * 2);
case Double d -> System.out.println("Double: " + d / 2);
default -> System.out.println("Unknown type");
}
}
}
public class Basic {
public static void main(String[] args) {
Object obj = ;
switch (obj) {
case String s -> System.out.println("String: " + s.toUpperCase());
case Integer i -> System.out.println("Integer: " + i * 2);
case Double d -> System.out.println("Double: " + d / 2);
default -> System.out.println("Unknown type");
}
}
}
type_pattern
A type pattern in switch matches the runtime type and binds a pattern variable in one step.
Switch Expressions with Patterns
Expression.java
public class Expression {
static String describe(Object obj) {
return switch (obj) {
case String s -> "String of length " + s.length();
case Integer i -> "Integer: " + i;
case Double d -> "Double: " + d;
case null -> "null value";
default -> "Unknown type: " + obj.getClass().getSimpleName();
};
}
public static void main(String[] args) {
System.out.println(describe("hello"));
System.out.println(describe(42));
System.out.println(describe(3.14));
System.out.println(describe(null));
System.out.println(describe(true));
}
}
Benefits
- Type testing: match on types, not just constants
- Pattern variables: automatic casting in each case
- Null handling:
case nullis explicit - Exhaustiveness: compiler checks all cases are covered
Null Handling
Null.java
public class Null {
static void process(Object obj) {
switch (obj) {
case null -> System.out.println("Got null");
case String s -> System.out.println("String: " + s);
case Integer i -> System.out.println("Integer: " + i);
default -> System.out.println("Other: " + obj);
}
}
public static void main(String[] args) {
process("test");
process(123);
process(null); // handled explicitly
}
}
null_case
The explicit `case null` pattern allows safe null handling without NullPointerException.
Case Ordering
Ordering.java
public class Ordering {
static void check(Object obj) {
switch (obj) {
case String s when s.isEmpty() -> System.out.println("Empty string");
case String s -> System.out.println("Non-empty string: " + s);
case Integer i when i < 0 -> System.out.println("Negative");
case Integer i -> System.out.println("Non-negative: " + i);
case null -> System.out.println("null");
default -> System.out.println("Other");
}
}
public static void main(String[] args) {
check("");
check("hello");
check(-5);
check(10);
check(null);
}
}
dominance
Case ordering matters - more specific patterns must come before more general ones to avoid dominance errors.
Exhaustiveness Checking
Exhaustiveness.java
public class Exhaustiveness {
sealed interface Shape permits Circle, Rectangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
static double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
};
}
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rect = new Rectangle(4.0, 6.0);
System.out.println("Circle area: " + area(circle));
System.out.println("Rectangle area: " + area(rect));
}
}
exhaustiveness
The compiler verifies all possible types are handled, requiring either complete coverage or a default case.
Exercise: Practical.java
Build a shape area calculator that handles Circle, Rectangle, and Triangle types using switch pattern matching