Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A type pattern is a language construct in Java that combines type checking and conditional extraction into a single operation. It consists of a predicate that tests whether a target object matches a specific type and, upon a successful match, automatically casts and binds the target to a newly declared local variable, known as a pattern variable.

Syntax

A type pattern consists of a type name followed by a variable declaration:
TargetType patternVariable

Mechanics in instanceof

Introduced in Java 16, type patterns eliminate the need for explicit casting after an instanceof check. If the runtime type of the target object is assignment-compatible with the TargetType, the pattern matches, and the patternVariable is initialized with the casted value.
// Traditional type checking and casting
if (obj instanceof String) {
    String s = (String) obj; 
    System.out.println(s.length());
}

// Type pattern matching
if (obj instanceof String s) {
    System.out.println(s.length());
}

Mechanics in switch

Standardized in Java 21, type patterns can be used as case labels within switch statements and expressions. This allows a switch block to route execution based on the runtime type of the selector expression.
Object obj = "Hello";

switch (obj) {
    case String s  -> System.out.println("String of length: " + s.length());
    case Integer i -> System.out.println("Integer value: " + i);
    case null      -> System.out.println("Null reference");
    default        -> System.out.println("Unmatched type");
}

Flow Scoping

Pattern variables utilize flow scoping (or flow-sensitive typing). The scope of a pattern variable is strictly limited to the execution paths where the compiler can definitively prove that the pattern has matched. Short-circuit evaluation:
// 's' is in scope on the right side of the && operator 
// because it only evaluates if the pattern match succeeds.
if (obj instanceof String s && s.length() > 5) {
    System.out.println(s);
}

// Compilation Error: 's' is NOT in scope on the right side of the || operator
// because the right side evaluates only if the pattern match fails.
if (obj instanceof String s || s.length() > 5) { }
Inverted scoping:
public void process(Object obj) {
    if (!(obj instanceof String s)) {
        return; // Execution halts if the pattern fails
    }
    
    // 's' is in scope for the remainder of the method block
    // because reaching this point guarantees the pattern matched.
    System.out.println(s.toUpperCase());
}

Guarded Patterns

Type patterns in switch blocks can be refined using the when keyword to apply additional boolean conditions, known as guards. The pattern variable is in scope within the when clause.
switch (obj) {
    case String s when s.length() > 10 -> System.out.println("Long string");
    case String s                      -> System.out.println("Short string");
    default                            -> System.out.println("Not a string");
}
Note: Pattern dominance rules apply. A guarded type pattern must appear before an unguarded type pattern of the same type, otherwise the compiler will throw an unreachable code error.

Nullability Rules

  • instanceof: A type pattern will never match a null reference. If obj is null, obj instanceof String s evaluates to false, and s is not initialized.
  • switch: A type pattern case String s will not match a null selector expression. To handle null in a pattern-matching switch, an explicit case null must be provided, or it can be combined with a type pattern using case null, String s.
Master Java with Deep Grasping Methodology!Learn More