Study guide

2.3 Pattern Matching for switch and instanceof

Oracle Java SE 21 Developer 1Z0-830

Track your progress as you read

Finished sections get a check mark.

1 Chapter 1: Java Basics, Primitive Types, and Operators

1.1 Program Structure, Primitives, Literals, Scopes, and var 1.2 Operators, Precedence, and Type Casting 1.3 Strings, Text Blocks, and StringBuilder 1.4 Date and Time APIs (java.time)

2 Chapter 2: Controlling Program Flow & Pattern Matching

2.1 Conditional Logic: if-else and Ternary Operators 2.2 Switch Statements and Switch Expressions 2.3 Pattern Matching for switch and instanceof 2.4 Loops and Branching Statements

3 Chapter 3: Object-Oriented Design & Class Architecture

3.1 Class Declarations, Constructors, and Object Lifecycle 3.2 Encapsulation and Access Modifiers 3.3 Inheritance, Method Overriding, and Polymorphism 3.4 Abstract Classes and Interfaces 3.5 Nested, Inner, Local, and Anonymous Classes 3.6 Method Overloading, Overload Resolution, and Varargs

4 Chapter 4: Modern Java Data Modeling: Records, Enums, and Sealed Types

4.1 Java Records, Canonical/Compact Constructors, and Component Accessors 4.2 Java Enums: Fields, Methods, and Abstract Constant-Specific Behavior 4.3 Sealed Classes and Interfaces: permits, final, non-sealed, and Hierarchy Architecture 4.4 Advanced Record Patterns (JEP 440), Nested Deconstruction, and Sealed Hierarchy Exhaustiveness

5 Chapter 5: Exception Handling and Annotations

5.1 Exception Hierarchy: Checked vs. Unchecked Exceptions 5.2 Exception Handling: try, catch, finally, and Multi-Catch 5.3 Resource Management: try-with-resources and AutoCloseable 5.4 Custom Exceptions and Best Practices 5.5 Java Annotations: Built-in, Meta-Annotations, and Custom Types

6 Chapter 6: Generics and the Collections Framework

6.1 Generics Fundamentals and Generic Methods 6.2 Wildcards, Type Bounds, and Type Erasure 6.3 Core Collections: Lists, Sets, Queues, and Maps 6.4 Sequenced Collections in Java 21 6.5 Sorting, Comparable, and Comparator 6.6 Arrays: Creation, Multidimensional Forms, and the Arrays Utility Class

7 Chapter 7: Functional Programming & Lambda Expressions

7.1 Built-in Functional Interfaces in java.util.function 7.2 Lambda Expressions, Variable Scopes, and Effectively Final 7.3 Method References: Four Varieties and Edge Cases 7.4 Working with java.util.Optional

8 Chapter 8: The Stream API and Data Pipelines

8.1 Stream Creation and Intermediate Operations 8.2 Terminal Operations and Reductions 8.3 Advanced Collectors, Grouping, Partitioning, and Teeing 8.4 Primitive Streams and Parallel Stream Processing

9 Chapter 9: Concurrency, Multithreading, and Virtual Threads

9.1 Thread Fundamentals, Creation, and Lifecycle 9.2 Thread Safety, Synchronization, and the Java Memory Model 9.3 Concurrency Utilities: ExecutorService, Callable, and Future 9.4 Concurrent Collections, Atomics, and Explicit Locks 9.5 Virtual Threads in Java 21 (Carrier Threads, Pinning, and Best Practices)

10 Chapter 10: The Java Platform Module System (JPMS)

10.1 Module Architecture and module-info.java Declarations 10.2 Module Directives: requires, exports, opens, provides, and uses 10.3 Modular Compilation, Packaging, and CLI Execution 10.4 Module Types, Migration Strategies, ServiceLoader SPI, and JLink

11 Chapter 11: Java I/O, NIO.2, and Object Serialization

11.1 Classic Java I/O Streams and Readers/Writers 11.2 Java NIO.2 File API: Path, Paths, and File System Operations 11.3 Advanced NIO.2 Stream Operations and Directory Traversal 11.4 Java Object Serialization and Record Serialization

12 Chapter 12: Implementing Localization and Internationalization

12.1 Locales and Resource Bundles 12.2 Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages

2.3 Pattern Matching for switch and instanceof

Key Takeaways Explain This Topic AI

Pattern matching for instanceof binds a typed pattern variable upon a successful type test, with scope determined by compiler flow analysis.

Pattern matching for switch (finalized in Java 21 via JEP 441) allows switching over any reference type using type patterns, record patterns, and explicit null labels.

Guarded patterns utilize the when clause to attach boolean conditions to pattern cases without nested if statements.

Pattern dominance is strictly enforced by the compiler: general supertypes and total patterns must not appear prior to more specific subtype patterns.

Exhaustiveness in pattern matching switch is satisfied when all possible hierarchy types (e.g., in sealed classes) or a total pattern/default case are handled.

Last updated: September 2026

Pattern Matching for switch and instanceof

Pattern matching simplifies Java code by combining type testing, casting, and local variable binding into a single declarative operation. Java 16 standardized pattern matching for instanceof (JEP 394), and Java 21 finalized pattern matching for switch (JEP 441) along with Record Patterns (JEP 440) . These features form a central pillar of the 1Z0-830 certification exam.

  • 1. Pattern Matching for instanceof
  • The Problem Pattern Matching Solves

In legacy Java, checking a type and accessing its specific members required a three-step boilerplate pattern: test with instanceof , explicitly cast to the target type, and assign to a new local variable. // Legacy Java approach:

  • if (obj instanceof String) {
  • String s = (String) obj; // Manual explicit cast
  • System.out.println(s.toUpperCase());
  • }

// Modern Pattern Matching approach:

if (obj instanceof String s) {

System.out.println(s.toUpperCase()); // 's' is automatically cast and bound!

  • }
  • Flow Scoping Rules

Pattern variables do not follow standard lexical block scoping; instead, they use flow scoping . A pattern variable is in scope only on the execution paths where the compiler can guarantee the pattern has matched (definitely assigned) .

Logical AND ( && ) Scoping : The right operand is only evaluated if the left operand evaluates to true . Therefore, the pattern variable is in scope on the right side of && : if (obj instanceof String s && s.length() > 5) { // LEGAL: s is in scope on right side of &&

  • System.out.println(s.toUpperCase());
  • }

Logical OR ( || ) Scoping : The right operand is evaluated when the left operand evaluates to false . Therefore, the pattern variable is NOT in scope on the right side of || : // if (obj instanceof String s || s.length() > 5) { } // COMPILE ERROR: cannot find symbol 's'

Negation and Early Return Scoping : If an if statement tests !(obj instanceof String s) and terminates with a return , throw , or break , s remains in scope for the rest of the method: void printLength(Object obj) {

  • if (!(obj instanceof String s)) {
  • return; // Exits if NOT a String
  • }

// 's' is in scope here because execution only reaches here if obj WAS a String!

  • System.out.println("String length: " + s.length());
  • }

Shadowing and Scope Clashes : A pattern variable cannot have the same name as an existing local variable in the same scope, but it may shadow a class field.

2. Pattern Matching for switch (Java 21 JEP 441)

In Java 21, the selector expression of a switch construct can be any reference type , and case labels can be type patterns , record patterns , constants , or null . static String formatValue(Object obj) {

  • return switch (obj) {
  • case Integer i -> String.format("Integer: %d", i);
  • case Long l -> String.format("Long: %d", l);
  • case Double d -> String.format("Double: %.2f", d);
  • case String s -> String.format("String: '%s'", s);
  • case null -> "Null reference";
  • default -> obj.toString();
  • };
  • }
  • 3. Guarded Patterns with the when Clause

A guarded pattern allows developers to specify a conditional boolean expression on a case label using the when contextual keyword (replacing the preview && syntax): static void classify(Object obj) {

  • switch (obj) {
  • case String s when s.length() > 10 -> System.out.println("Long string: " + s);
  • case String s when !s.isEmpty() -> System.out.println("Non-empty string: " + s);
  • case String s -> System.out.println("Empty string");
  • case Integer i when i > 0 -> System.out.println("Positive int: " + i);
  • case Integer i -> System.out.println("Zero or negative int: " + i);
  • default -> System.out.println("Other object");
  • }
  • }

Guard Expression Rules:

The expression following when must evaluate to a boolean or Boolean .

The pattern variable declared in the case label is in scope within the when expression.

If the type matches but the when expression evaluates to false , execution skips this case and tests subsequent cases.

4. Pattern Dominance and Ordering Rules

The compiler checks case patterns top-to-bottom and enforces strict dominance rules . If a preceding case pattern matches a superset of all values that a subsequent pattern can match, the preceding pattern dominates the subsequent one, causing a compile-time error for unreachable code .

Dominance Ordering Hierarchy:

Specific Guarded Pattern must precede General Unguarded Pattern : // INVALID: Compilation Error!

  • switch (obj) {
  • case String s -> System.out.println("Any string");

case String s when s.length() > 5 -> System.out.println("Long string"); // ERROR: Dominated!

}

// CORRECT ORDER:

  • switch (obj) {
  • case String s when s.length() > 5 -> System.out.println("Long string");
  • case String s -> System.out.println("Any string");
  • }

Subtype Pattern must precede Supertype Pattern : // INVALID: Compilation Error!

  • switch (obj) {
  • case CharSequence cs -> System.out.println("CharSequence");

case String s -> System.out.println("String"); // ERROR: Dominated by CharSequence!

}

// CORRECT ORDER:

  • switch (obj) {
  • case String s -> System.out.println("String");
  • case CharSequence cs -> System.out.println("CharSequence");
  • }

Total Patterns and Default :

A pattern case Object o is a total pattern for any Object selector. A total pattern matches all non-null objects and dominates all subsequent type patterns, meaning it must appear last.

5. Null Handling in Pattern Matching Switch

Historically, passing null to a switch statement immediately triggered a NullPointerException . In Java 21, nullability is explicitly integrated into pattern matching:

  • Explicit case null : Handles null values cleanly without throwing an exception. switch (obj) {
  • case null -> System.out.println("Object is null");
  • case String s -> System.out.println("String: " + s);
  • default -> System.out.println("Other");
  • }

Combined case null, default : Allows handling null and unmatched objects in a single fallback branch: switch (obj) {

  • case String s -> System.out.println("String: " + s);
  • case null, default -> System.out.println("Null or unsupported type");
  • }

Implicit Null Failure : If the switch selector evaluates to null and neither case null nor case null, default is present, the switch throws a NullPointerException before evaluating any patterns (even if an unguarded default is present!).

6. Record Patterns and Deconstruction (JEP 440)

Java 21 allows deconstructing record components directly inside pattern matching: record Point(int x, int y) {}

record Line(Point start, Point end) {}

  • static void printPoint(Object obj) {
  • if (obj instanceof Point(int x, int y)) {
  • System.out.println("Point at: " + x + ", " + y);
  • }
  • }
  • static void printLine(Object obj) {
  • if (obj instanceof Line(Point(int x1, int y1), Point(var x2, var y2))) {
  • System.out.printf("Line from (%d,%d) to (%d,%d)%n", x1, y1, x2, y2);
  • }
  • }
  • 7. Exhaustiveness and Sealed Hierarchies

A pattern matching switch expression must be exhaustive . When switching over a sealed class or interface , if all permitted direct subtypes are covered by case patterns, the switch expression is exhaustive without requiring a default label: sealed interface Shape permits Circle, Rectangle, Triangle {}

  • record Circle(double radius) implements Shape {}
  • record Rectangle(double w, double h) implements Shape {}
  • record Triangle(double b, double h) implements Shape {}
  • double getArea(Shape shape) {
  • return switch (shape) {
  • case Circle c -> Math.PI * c.radius() * c.radius();
  • case Rectangle r -> r.w() * r.h();
  • case Triangle t -> 0.5 * t.b() * t.h();

// No default required! All permitted direct subtypes of Shape are handled.

  • };
  • }

If a new subtype is added to the Shape interface later, the compiler immediately flags the switch expression with a compile-time error, preventing missing branch bugs across codebases.

Loading diagram... Pattern Matching Switch Evaluation and Dominance Flow