Close

Java 16 - Instanceof Pattern Matching

[Last Updated: Dec 8, 2025]

Java 16 finalized Pattern Matching for instanceof (JEP 394).

What is pattern matching?

The process of testing a value against a pattern is known as pattern matching. If a value successfully matches a pattern, then the process of pattern matching initializes the pattern variables, if any, declared by the pattern.

Pattern matching for instnaceof

The instanceof operator checks if an object matches a type. If it does, the object is automatically cast to that type and assigned to a new variable.

That means, we can now write this:

if (obj instanceof String s) {
 }

instead of:

if (obj instanceof String) { 
  String s = (String) obj; 
}

Quick walk through

In following code if obj is an instance of String, then it is cast to String and assigned to the binding variable s.

public class InstanceOfPatternMatching {
    public static void main(String... args) {
        Object str = "Hello, World!";
        if (str instanceof String s) {
            System.out.println("The length of the string is: " + s.length());
        }
    }
}

Output

$ javac InstanceOfPatternMatching.java

$ java InstanceOfPatternMatching
The length of the string is: 13
JDK 16

Flow Scoping

Flow scoping is the compiler’s ability to introduce and track the pattern variable’s scope based on the program’s execution flow.

public class FlowScopeExample1 {
    public static void main(String[] args) {
        Object obj = "my string";
        if (obj instanceof String s) {
            System.out.println("string length in if part: " + s.length());
        } else {
            //s is not available here, compile error if uncommented next line
            //System.out.println("string length in else part: "+s.length());
        }
    }
}

Output

$ javac FlowScopeExample1.java

$ java FlowScopeExample1
string length in if part: 9
JDK 16

Flow Scoping, negated conditions

If the instanceof check is negated, the pattern variable is in scope in the else block where the condition effectively becomes true.

public class FlowScopeExample2 {
    public static void main(String[] args) {
        Object obj = "my string";
        if (!(obj instanceof String s)) {
            //s is not available here, compile error if uncommented next line
            //System.out.println("string length in if part: "+s.length());
        } else {
            System.out.println("string length in else part: " + s.length());

        }
    }
}

Output

$ javac FlowScopeExample2.java

$ java FlowScopeExample2
string length in else part: 9
JDK 16

Flow scoping through logical operators AND (&&)

public class FlowScopeLogicalAndExample1 {
    public static void main(String[] args) {
        Object obj = "my string";
        if (obj instanceof String s && s.length() > 3) {
            System.out.println("string length in if part: " + s.length());
        } else {
            //s is not available here, compile error if uncommented next line
            //System.out.println("string length in else part: "+s.length());
        }
    }
}

Output

$ javac FlowScopeLogicalAndExample1.java

$ java FlowScopeLogicalAndExample1
string length in if part: 9
JDK 16
public class FlowScopeLogicalAndExample2 {
    public static void main(String[] args) {
        Object obj = "my string";
        if (obj instanceof String s && s.length() <= 3) {
            System.out.println("string length in if part: " + s.length());
        } else {
            //s is not available here, compile error if uncommented next line
            // System.out.println("string length in else part: "+s.length());
            System.out.println("obj might not be String in else part, "
                    + "no s available here");
        }
    }
}

Output

$ javac FlowScopeLogicalAndExample2.java

$ java FlowScopeLogicalAndExample2
obj might not be String in else part, no s available here
JDK 16

Multiple && operators

public class MultipleLogicalAndExample {
    public static void main(String[] args) {
        Object obj = "/a/b/c/";
        if (obj instanceof String s && !s.isEmpty() &&
                s.startsWith("/") && s.endsWith("/")) {
            System.out.println("string length: " + s.length());
        }
    }
}

Output

$ javac MultipleLogicalAndExample.java

$ java MultipleLogicalAndExample
string length: 7
JDK 16

Pattern Variable Does NOT Flow Through OR (||)

With OR, Java cannot guarantee the pattern was evaluated and matched, so it does not make sense the pattern variable should exist on the right side. Therefore, it's a compile time error if we use logical OR:

public class FlowScopeLogicalOrExample {
    public static void main(String[] args) {
        Object obj = "my string";
        if (obj instanceof String s || s.length() > 3) {
            System.out.println("string length in if part: " + s.length());
        }
    }
}

Output

$ javac FlowScopeLogicalOrExample.java
FlowScopeLogicalOrExample.java:4: error: cannot find symbol
if (obj instanceof String s || s.length() > 3) {
^
symbol: variable s
location: class FlowScopeLogicalOrExample
FlowScopeLogicalOrExample.java:5: error: cannot find symbol
System.out.println("string length in if part: " + s.length());
^
symbol: variable s
location: class FlowScopeLogicalOrExample
2 errors
JDK 16

More Scenarios of using multiple &&

Multiple && in equals method

import java.util.Objects;

public class MultipleLogicalAndExample2 {
    static class Person {
        private String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public boolean equals(Object other) {
            return other instanceof Person person &&
                    this.age == person.age &&
                    Objects.equals(this.name, person.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }

    public static void main(String[] args) {
        Person p1 = new Person("Jim", 22);
        Person p2 = new Person("Jim", 22);
        System.out.println(p1.equals(p2));
    }
}

Output

$ javac MultipleLogicalAndExample2.java

$ java MultipleLogicalAndExample2
true
JDK 16

Using pattern matching in Ternary operator

import java.math.BigDecimal;

public class TernaryOperatorExample {
    public static void main(String[] args) {
        Number num = getNumber();
        String s = num instanceof BigDecimal bd ? "BigDecimal: " +
                bd.toPlainString() : "Not a BigDecimal";
        System.out.println(s);
    }

    private static Number getNumber() {
        return new BigDecimal("5");
    }
}

Output

$ javac TernaryOperatorExample.java

$ java TernaryOperatorExample
BigDecimal: 5
JDK 16

Multiple instanceof Pattern matching

public class MultipleInstanceOfExample {
    public static void main(String[] args) {
        Object input = getInput();
        if (input instanceof Map<?, ?> map && map.containsKey("fruits")
                && map.get("fruits") instanceof List<?> list
                && list.size() > 1
                && list.stream().allMatch(
                fruit -> fruit instanceof String fruitName
                        && fruitName.length() > 1)) {

            List<String> fruitList = list.stream()
                                         .map(String::valueOf).toList();

            System.out.printf("map size: %s, fruit list: %s%n",
                    map.size(),
                    String.join("|", fruitList));
        }
    }

    private static Object getInput() {
        return Map.of("fruits", List.of("apple", "mango"));
    }
}

Output

$ javac MultipleInstanceOfExample.java

$ java MultipleInstanceOfExample
map size: 1, fruit list: apple|mango
JDK 16

Expression-Pattern Type Compatibility

Compilation error: Expression type is same as Pattern type

Following will end up with compilation error:

public class SameTypeAsExpressionType {
    public static void main(String... args) {
        String str = "Hello, World!";
        if (str instanceof String s) {
            System.out.println("The length of the string is: " + s.length());
        }
    }
}

Output

$ javac SameTypeAsExpressionType.java
SameTypeAsExpressionType.java:4: error: pattern type String is a subtype of expression type String
if (str instanceof String s) {
^
1 error
JDK 16

Since myString is already declared as a String, the instanceof String s check is redundant and will always be true, defeating the operator's purpose of conditional type checking.

Expression Type vs Pattern Type

Pattern type is the type you're trying to match.
Expression type is the type of the variable/expression you are testing.
So in obj instanceof String s, String s is the pattern and String is the pattern type, whereas, Expression is on the left and the expression type is the type of obj.

Compilation error: Expression type is the subtype of Pattern type

Following will also end up with compilation error:

public class SubTypeOfExpressionType {
    public static void main(String... args) {
        String str = "Hello, World!";
        if (str instanceof CharSequence s) {
            System.out.println("The length of the string is: " + s.length());
        }
    }
}

Output

$ javac SubTypeOfExpressionType.java
SubTypeOfExpressionType.java:4: error: pattern type CharSequence is a subtype of expression type String
if (str instanceof CharSequence s) {
^
1 error
JDK 16

It is a compile-time error for a pattern instanceof expression to compare an expression of type S against a pattern of type T, where S is a subtype of T. This instanceof expression will always succeed and is then pointless.

Relaxed Expression-Pattern Type Compatibility since Java 21

Our last two examples failed to compile due to redundant instanceof pattern matching check. Starting Java 21, the compiler now accepts this code, allowing for more consistent use of pattern matching even when the type check is statically known to succeed.

public class SameTypeAsExpressionType {
    public static void main(String... args) {
        String str = "Hello, World!";
        if (str instanceof String s) {
            System.out.println("The length of the string is: " + s.length());
        }
    }
}

Output

$ javac SameTypeAsExpressionType.java

$ java SameTypeAsExpressionType
The length of the string is: 13
JDK 21
public class SubTypeOfExpressionType {
    public static void main(String... args) {
        String str = "Hello, World!";
        if (str instanceof CharSequence s) {
            System.out.println("The length of the string is: " + s.length());
        }
    }
}

Output

$ javac SubTypeOfExpressionType.java

$ java SubTypeOfExpressionType.java
The length of the string is: 13
JDK 21

As seen above we are able to compile the code in Java 21 (OpenJDK) and later version. However, I could not find a publically published OpenJDK document saying the subtype-restriction was removed in Java 21

See Also