Pattern Matching
instanceof Pattern Matching
When processing heterogeneous collections or API responses, you often need to check types and cast objects repeatedly. instanceof pattern matching combines type checking and casting into a single operation, eliminating redundant code and potential ClassCastException bugs.
Old vs New Style
OldVsNew.java
Replay: real traced execution (multi-file project)
public class OldVsNew {
static void oldStyle(Object obj) {
if (obj instanceof String) {
String s = (String) obj; // redundant cast
System.out.println("OLD: " + s.toUpperCase());
}
}
static void newStyle(Object obj) {
if (obj instanceof String s) {
System.out.println("NEW: " + s.toUpperCase());
}
}
public static void main(String[] args) {
Object obj = "pattern matching";
oldStyle(obj);
newStyle(obj);
}
}
obj ← pattern matching
15public static void main(String[] args) {16 Object obj→ pattern matching = "pattern matching";17 oldStyle(objpattern matching);18 newStyle(obj);static void oldStyle(Object obj)
1public class OldVsNew {2 static void oldStyle(Object objpattern matching) {3 if (obj instanceof String) {s ← pattern matching
2static void oldStyle(Object obj) {3 if (obj instanceof String) {4 String s→ pattern matching = (String) obj; // redundant cast5 System.out.println("OLD: " + s.toUpperCase());6 }outputOLD: PATTERN MATCHINGoldStyle(obj);
16 Object obj = "pattern matching";17 oldStyle(objpattern matching);18 newStyle(objpattern matching);19}static void newStyle(Object obj)
9static void newStyle(Object objpattern matching) {10 if (obj instanceof String s) {if (obj instanceof String s)
9static void newStyle(Object obj) {10 if (obj instanceof String s) {11 System.out.println("NEW: " + s.toUpperCase());12 }outputNEW: PATTERN MATCHINGnewStyle(obj);
17 oldStyle(obj);18 newStyle(objpattern matching);19}
The pattern variable s is automatically cast and scoped to the if block.
pattern_variable
A pattern variable is automatically declared, cast, and scoped when the instanceof test succeeds.
Basic Usage
Basic.java
Replay: real traced execution (multi-file project)
public class Basic {
public static void main(String[] args) {
Object obj = "hello";
if (obj instanceof String s) {
System.out.println("Length: " + s.length());
System.out.println("Upper: " + s.toUpperCase());
}
}
}
public class Basic {
public static void main(String[] args) {
Object obj = "pattern matching";
if (obj instanceof String s) {
System.out.println("Length: " + s.length());
System.out.println("Upper: " + s.toUpperCase());
}
}
}
public class Basic {
public static void main(String[] args) {
Object obj = 123;
if (obj instanceof String s) {
System.out.println("Length: " + s.length());
System.out.println("Upper: " + s.toUpperCase());
}
}
}
obj ← hello
1public class Basic {2 public static void main(String[] args) {3 Object obj→ hello = "hello"; //@obj="hello", "pattern matching", 1234 if (obj instanceof String s) {if (obj instanceof String s)
3Object obj = "hello"; //@obj="hello", "pattern matching", 1234if (obj instanceof String s) {5 System.out.println("Length: " + s.length());6 System.out.println("Upper: " + s.toUpperCase());7}outputLength: 5 Upper: HELLO
obj ← pattern matching
1public class Basic {2 public static void main(String[] args) {3 Object obj→ pattern matching = "pattern matching";4 if (obj instanceof String s) {if (obj instanceof String s)
3Object obj = "pattern matching";4if (obj instanceof String s) {5 System.out.println("Length: " + s.length());6 System.out.println("Upper: " + s.toUpperCase());7}outputLength: 16 Upper: PATTERN MATCHING
obj ← 123
1public class Basic {2 public static void main(String[] args) {3 Object obj→ 123 = 123;4 if (obj instanceof String s) {
Benefits
- No redundant cast: type checking and casting happen together
- Shorter code: fewer lines, clearer intent
- Safer: compiler ensures the pattern variable is only used where valid
Scope Rules
The pattern variable is in scope where the instanceof test is definitely true:
Scope.java
Replay: real traced execution (multi-file project)
public class Scope {
public static void main(String[] args) {
Object obj = "scoped";
if (obj instanceof String s) {
System.out.println("In if: " + s);
}
if (obj instanceof String s && s.length() > 3) {
System.out.println("Length check: " + s);
}
if (!(obj instanceof String s)) {
} else {
System.out.println("In else: " + s);
}
}
}
public class Scope {
public static void main(String[] args) {
Object obj = "hi";
if (obj instanceof String s) {
System.out.println("In if: " + s);
}
if (obj instanceof String s && s.length() > 3) {
System.out.println("Length check: " + s);
}
if (!(obj instanceof String s)) {
} else {
System.out.println("In else: " + s);
}
}
}
obj ← scoped
1public class Scope {2 public static void main(String[] args) {3 Object obj→ scoped = "scoped"; //@obj="scoped", "hi"4 if (obj instanceof String s) {if (obj instanceof String s)
3Object obj = "scoped"; //@obj="scoped", "hi"4if (obj instanceof String s) {5 System.out.println("In if: " + sscoped);6}outputIn if: scopedif (obj instanceof String s && s.length() > 3)
6}7if (obj instanceof String s && s.length() > 3) {8 System.out.println("Length check: " + sscoped);9}outputLength check: scopedelse
10if (!(obj instanceof String s)) {11} else {12 System.out.println("In else: " + sscoped);13}outputIn else: scoped
obj ← hi
1public class Scope {2 public static void main(String[] args) {3 Object obj→ hi = "hi";4 if (obj instanceof String s) {if (obj instanceof String s)
3Object obj = "hi";4if (obj instanceof String s) {5 System.out.println("In if: " + shi);6}outputIn if: hielse
10if (!(obj instanceof String s)) {11} else {12 System.out.println("In else: " + shi);13}outputIn else: hi
scope_flow
Pattern variables follow "flow scoping" - they are in scope wherever the compiler can prove the pattern matched.
Null Safety
NullSafe.java
Replay: real traced execution (multi-file project)
public class NullSafe {
static void process(Object obj) {
if (obj instanceof String s) {
System.out.println("String: " + s);
} else {
System.out.println("Not a string (or null)");
}
}
public static void main(String[] args) {
process("hello");
process(123);
process(null); // instanceof is false for null
}
}
public static void main(String[] args)
10public static void main(String[] args) {11 process("hello");12 process(123);static void process(Object obj)
pass 1 of 31public class NullSafe {2 static void process(Object objhello) {3 if (obj instanceof String s) {All 3 passes — pass 1 is the card above pass objs1 hello hello 2 123 — 3 null — if (obj instanceof String s)
2static void process(Object obj) {3 if (obj instanceof String s) {4 System.out.println("String: " + shello);5 } else {outputString: helloprocess("hello");
10public static void main(String[] args) {11 process("hello");12 process(123);13 process(null); // instanceof is false for nullelse
pass 1 of 24 System.out.println("String: " + s);5} else {6 System.out.println("Not a string (or null)");7}outputNot a string (or null)process(123);
11 process("hello");12 process(123);13 process(null); // instanceof is false for null14}else
pass 2 of 24 System.out.println("String: " + s);5} else {6 System.out.println("Not a string (or null)");7}outputNot a string (or null)process(null); // instanceof is false for null
12 process(123);13 process(null); // instanceof is false for null14}
Multiple Type Checks
MultipleTypes.java
Replay: real traced execution (multi-file project)
public class MultipleTypes {
static String describe(Object obj) {
if (obj instanceof String s) {
return "String of length " + s.length();
} else if (obj instanceof Integer i) {
return "Integer value " + i;
} else if (obj instanceof Double d) {
return "Double value " + d;
} else {
return "Unknown type";
}
}
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(true));
}
}
public static void main(String[] args)
14public static void main(String[] args) {15 System.out.println(describe("hello"));16 System.out.println(describe(42));static String describe(Object obj)
pass 1 of 41public class MultipleTypes {2 static String describe(Object objhello) {3 if (obj instanceof String s) {All 4 passes — pass 1 is the card above pass objid1 hello — — 2 42 42 — 3 3.14 — 3.14 4 true — — System.out.println(describe("hello"));
14public static void main(String[] args) {15 System.out.println(describe("hello"));16 System.out.println(describe(42));17 System.out.println(describe(3.14));outputString of length 5if (obj instanceof Integer i)
4 return "String of length " + s.length();5} else if (obj instanceof Integer i) {6 return "Integer value " + i42;7} else if (obj instanceof Double d) {System.out.println(describe(42));
15System.out.println(describe("hello"));16System.out.println(describe(42));17System.out.println(describe(3.14));18System.out.println(describe(true));outputInteger value 42if (obj instanceof Double d)
6 return "Integer value " + i;7} else if (obj instanceof Double d) {8 return "Double value " + d3.14;9} else {System.out.println(describe(3.14));
16 System.out.println(describe(42));17 System.out.println(describe(3.14));18 System.out.println(describe(true));19}outputDouble value 3.14System.out.println(describe(true));
17 System.out.println(describe(3.14));18 System.out.println(describe(true));19}outputUnknown type
Exercise: Practical.java
Create a method that processes a list of Objects and returns formatted strings based on their actual types