Pattern Matching (instanceof, switch, record patterns) practice questions

From OCP Java SE 21 (1Z0-830) · 27 questions on this topic

Pattern Matching (instanceof, switch, record patterns) practice questions from OCP Java SE 21 (1Z0-830). This pack has 27 questions tagged Pattern Matching (instanceof, switch, record patterns), drawn from its timed mock exams. 8 of them are worked through in full below — the question, every option, why each is right or wrong, and the explanation.

Worked examples for Pattern Matching (instanceof, switch, record patterns)

  1. Question 1

    What does this print? ```java public class Main { record Pair(int a, int b) {} public static void main(String[] args) { Object o = new Pair(2, 3); if (o instanceof Pair(int a, int b)) { System.out.println(a * b); } } } ```

    1. A. 6Correct answer

      The value is a Pair, so o instanceof Pair(int a, int b) matches and binds a=2, b=3; the body prints a * b = 6 (JEP 440).

    2. B. Compilation fails: record pattern not allowed in instanceof

      Assumes record patterns are switch-only, but JEP 440 explicitly extends them to instanceof, so the code compiles.

    3. C. 23

      23 would be string concatenation of the two components, but a * b is integer multiplication.

    4. D. 5

      5 is a + b, the sum, not the product that the code computes.

    Explanation

    Record deconstruction is not limited to switch; a record pattern can also appear in an instanceof test, where a matching value is deconstructed and its component bindings are flow-scoped into the guarded block. Here the value is that record type, so the pattern matches and both components are bound. The body multiplies them, so the output is their product.

  2. Question 2

    What does this print? ```java public class Main { static String f(Object o) { return switch (o) { case null -> "null"; case Integer i -> "int"; default -> "other"; }; } public static void main(String[] args) { System.out.println(f(null) + " " + f(7) + " " + f("x")); } } ```

    1. A. null int int

      Assumes the String argument matches case Integer i, but a String never matches an Integer pattern, so the third result is other, not int.

    2. B. other int other

      Assumes the default handles the null, but null is matched by its own case null label here, and the default never matches null anyway, so the first result is null.

    3. C. null int otherCorrect answer

      case null matches the null selector (no NPE) giving null, 7 boxes to an Integer giving int, and "x" matches no listed pattern so the default gives other (JEP 441).

    4. D. Throws NullPointerException

      NullPointerException is thrown only when a pattern switch lacks a case null label; this switch has one, so null is handled instead of throwing.

    Explanation

    An explicit case null label lets a pattern switch accept a null selector instead of throwing, while non-null values are routed by runtime type to the first matching label or to the default. A boxed integer matches the integer type label, and a value matching no listed pattern falls to the default. So each of the three calls resolves to a different arm.

  3. Question 3

    What does this print? ```java public class Main { record Point(int x, int y) {} static String f(Object o) { return switch (o) { case Point(int x, int y) -> x + "," + y; default -> "?"; }; } public static void main(String[] args) { System.out.println(f(new Point(3, 4))); } } ```

    1. A. ?

      The default is taken only when the record pattern fails, but a Point value always matches Point(int x, int y), so the default is not reached.

    2. B. Point[x=3, y=4]

      This is the record's generated toString() form, which this code never calls; the arm concatenates the bound components instead.

    3. C. 3,4Correct answer

      The Point value matches the record pattern, which deconstructs it by position into x=3 and y=4, so the arm yields 3 + "," + 4 (JEP 440).

    4. D. Compilation fails

      Assumes record patterns are not allowed, but record patterns in switch are standard, final in Java 21 (JEP 440), so it compiles.

    Explanation

    A record pattern matches when the value is of the record type and then deconstructs it, binding the components by position through the record's accessors. Because the value here is that exact record type, the pattern matches and the alternative default is never considered. The arm builds its result from the bound component values, not from the record's textual toString form.

  4. Question 4

    Why does this fail to compile? ```java public class Main { static String f(Object o) { return switch (o) { case Integer i -> "int"; case Integer i when i > 0 -> "pos"; default -> "other"; }; } public static void main(String[] args) { System.out.println(f(1)); } } ```

    1. A. Guarded patterns are not allowed in switch

      Guarded patterns using when are a standard part of pattern switch, so their presence is not the error.

    2. B. Two cases may never share the same type

      Two labels may share a type when the guarded one comes first; it is only this particular order that is illegal, not sharing a type as such.

    3. C. when can only be used with record patterns

      when attaches to any pattern label, including type patterns, not only record patterns, so this is not the reason.

    4. D. The unguarded Integer case dominates the later guarded one, making it unreachableCorrect answer

      The unguarded case Integer i matches every Integer, so the later guarded case Integer i when i > 0 is unreachable, and a dominated case label is a compile-time error (JEP 441).

    Explanation

    Case labels must be ordered so that no label is dominated by an earlier one, just as catch clauses must not be unreachable. An unguarded type pattern matches every value of that type, so any later label restricted by a guard on the same type can never be reached. Putting the general case ahead of the more specific guarded case is therefore a compile-time error; the guarded label must come first.

  5. Question 5

    What is the result of attempting to compile and run the following program? ```java public class Main { public static void main(String[] args) { Object obj = "test"; if (obj instanceof String s || s.isEmpty()) { System.out.println("yes"); } } } ```

    1. A. yes

      A || B with an instanceof pattern on the left does not extend the binding into B. The right-hand operand of || is evaluated precisely when the left side is false — that is, when obj instanceof String s has not matched and s is unbound. The compiler detects that s is not definitely assigned in s.isEmpty() and rejects the program before it can run.

    2. B. Compilation failsCorrect answer

      Under JEP 394 flow scoping, in A || B where A introduces a pattern variable, the variable is not in scope in B because B is evaluated only when A is false — precisely the case where the match failed and the variable was never bound. The compiler treats s as not definitely assigned in s.isEmpty() and issues a compile-time error.

    3. C. Throws NullPointerException

      Pattern variables are not null-initialized like instance fields; they are subject to definite-assignment analysis. Because s is not definitely assigned on the right-hand side of ||, the compiler rejects the reference as a scope violation before any bytecode is generated. There is no runtime at which a NullPointerException could occur.

    4. D. Nothing is printed

      Any outcome that implies the program ran presupposes successful compilation. The error here is a compile-time scope violation, so no class file is produced and no output of any kind is possible.

    Explanation

    JEP 394 flow scoping draws a deliberate asymmetry between && and ||: in A && B, the right operand is reached only when A succeeded, so a pattern variable introduced by A is in scope in B; in A || B, the right operand is reached only when A failed, so the same pattern variable is not in scope in B. Referencing the variable on the right-hand side of || is therefore a compile-time error regardless of what value the subject would hold at runtime. Any answer that assumes a runtime outcome — printed output, a thrown exception, or silent completion — is only possible if compilation succeeds, which it does not.

  6. Question 6

    What is printed? ```java public class Main { record Point(int x, int y) {} record Line(Point a, Point b) {} static int f(Object o) { return switch (o) { case Line(Point(var x1, var y1), Point(var x2, var y2)) -> (x2 - x1) + (y2 - y1); default -> -1; }; } public static void main(String[] args) { System.out.println(f(new Line(new Point(1, 1), new Point(4, 5)))); } } ```

    1. A. -1

      The default would require the nested Line pattern to fail, but the argument is a Line whose components are Points, so every level matches and the default is not reached.

    2. B. 8

      An arithmetic slip: the two differences are 3 and 4, which sum to 7, not 8.

    3. C. 3

      This keeps only the x-difference (x2 - x1) and drops the y term; both differences must be added.

    4. D. 7Correct answer

      The nested record pattern binds x1=1, y1=1, x2=4, y2=5, so the arm computes (4-1)+(5-1) = 3+4 = 7 (JEP 440).

    Explanation

    A nested record pattern deconstructs at every level, and it matches only when the value and each nested component match their patterns; var infers each component's type. Once matched, the bindings hold the innermost component values by position, which the arm then combines. Both coordinate differences contribute to the computed result.

  7. Question 7

    What does this print? ```java public class Main { record Point(int x, int y) {} static String f(Object o) { return switch (o) { case Point(int x, int y) when x == y -> "diagonal"; case Point(int x, int y) -> "point"; default -> "other"; }; } public static void main(String[] args) { System.out.println(f(new Point(3, 3)) + " " + f(new Point(1, 2))); } } ```

    1. A. other other

      Both arguments are Points, so a Point pattern always matches and the default is never selected.

    2. B. diagonal pointCorrect answer

      For Point(3,3) the guard x == y holds so the guarded arm yields diagonal; for Point(1,2) the guard fails so matching falls to the unguarded Point arm yielding point (JEP 440/441).

    3. C. diagonal diagonal

      The second call has unequal coordinates, so its guard x == y is false and it cannot yield diagonal.

    4. D. point point

      Both results would be point only if neither call satisfied the guard, but the first call has equal coordinates and takes the guarded arm.

    Explanation

    A guard on a record pattern is evaluated only after the pattern itself matches, and a false guard simply moves matching to the next label rather than throwing or jumping to the default. A value that satisfies the guard takes the guarded arm, while one that matches the pattern but fails the guard is caught by the later unguarded arm. So two points differing only in whether their coordinates are equal resolve to different arms.

  8. Question 8

    Which two statements about the case labels of a Java 21 pattern switch are correct? (Choose two.)

    1. A. A `when` guard may only follow a case label that has a pattern; `default when ...` does not compile.Correct answer

      A when guard is part of a pattern label and has nowhere to attach on default, so default when ... does not compile.

    2. B. `null` may not share a label with `default`; `case null, default ->` is rejected as an invalid case label combination.

      Inverts the actual rule. case null, default -> is the one sanctioned combination and compiles; it is case null combined with a pattern, such as case null, String s, that javac rejects as an invalid combination.

    3. C. A guarded pattern does not contribute to exhaustiveness: a permitted subtype covered only by a guarded arm still needs a `default` or an unguarded arm.Correct answer

      A guard can evaluate to false at runtime, so the compiler cannot count a guarded arm as covering its type; a permitted subtype reached only through a when clause still needs a default or an unguarded arm.

    4. D. `default` is exempt from dominance checking and may be written ahead of the pattern labels.

      The trap: default is not exempt. Placing it first makes every following pattern label unreachable, and javac reports this case label is dominated. default must be written last.

    Explanation

    A guard is part of a *pattern* label, so it has nowhere to attach on `default`. And because a guard can evaluate to false at runtime, the compiler cannot count a guarded arm as covering its type — a sealed hierarchy whose subtype is only reachable through a `when` clause is not exhaustively covered. Why the others are wrong: ``default` is exempt from dominance checking...` is the trap. `default` is not exempt: placing it first makes every following pattern label unreachable, and javac rejects the pattern label with `this case label is dominated by a preceding case label`. Write `default` last. ``null` may not share a label with `default`...` inverts the actual rule. `case null, default ->` is the one sanctioned combination and it compiles — it is `case null` combined with a *pattern*, such as `case null, String s ->`, that javac rejects with `invalid case label combination`. Exam tip: two separate rules about guards are examined constantly — a guard belongs to a pattern (never to `default`, never to a constant label), and a guarded arm never satisfies exhaustiveness. If a sealed switch stops compiling the moment you add `when` to its last arm, that is why.

Practise all 27 Pattern Matching (instanceof, switch, record patterns) questions

OCP Java SE 21 has the full set, inside timed mock exams that mirror real exam conditions — every question with a worked explanation.

Open OCP Java SE 21

Other topics in this pack