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

From OCP Java SE 17 (1Z0-829) · 16 questions on this topic

Pattern Matching (instanceof, switch, record patterns) practice questions from OCP Java SE 17 (1Z0-829). This pack has 16 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

    Which two statements about pattern matching for instanceof in Java 17 are correct? (Choose two.)

    1. A. The pattern variable is in scope only where the compiler can prove the match succeeded, which can include an else branch or code after an early returnCorrect answer

      JEP 394 uses flow scoping, so the binding exists exactly where definite-match analysis proves the pattern matched, which is why a negated test or an early return can place it in an else branch or in code after the if.

    2. B. If the tested expression is null, the pattern matches and the variable is bound to null

      instanceof is always false for null, with or without a pattern, so the variable is never bound to null.

    3. C. A pattern variable is a non-final local variable, so code inside its scope may reassign itCorrect answer

      The final design of JEP 394 made pattern variables ordinary non-final locals, so reassignment inside their scope is legal (though poor style).

    4. D. Java 17 also allows type patterns as case labels in switch without any special compiler flags

      Pattern matching for switch is only a preview in Java 17 (JEP 406), gated behind --enable-preview and outside 1Z0-829; only instanceof patterns are standard.

    Explanation

    Pattern matching for instanceof in Java 17 uses flow scoping, so a binding is visible exactly where definite-match analysis proves the match succeeded, and the variable it introduces is an ordinary non-final local. A null operand never matches a type pattern, so it yields false and binds nothing. Type patterns in switch remain a preview feature in Java 17 and are not part of the standard instanceof-only support.

  2. Question 2

    What is the result? ```java public class Main { public static void main(String[] args) { Object o = 42; if (!(o instanceof String s)) { System.out.println("not string"); } else { System.out.println(s.length()); } } } ```

    1. A. Compilation fails: s out of scope in else

      With a negated test, flow scoping proves the match succeeded on the path that reaches the else branch, so the pattern variable is legally in scope there; treating this as a scope error misreads flow scoping.

    2. B. not stringCorrect answer

      An Integer operand does not match the String pattern, so the test is false and the negated condition is true, taking the branch that reports the type mismatch (JEP 394 flow scoping).

    3. C. 42

      Printing the number would require printing the operand itself, which no branch of this code does.

    4. D. 2

      This is the length of the bound string on a successful match, but an Integer operand never matches the String pattern, so that branch never runs.

    Explanation

    Negating an instanceof pattern flips where flow scoping places the binding: the variable becomes usable in the branch reached only after a successful match, which for a negated test is the else. Because an Integer operand fails the String test, the negated condition is true and the mismatch branch executes. The else still compiles even though it never runs, because the compiler proves the match would have succeeded on that path.

  3. Question 3

    Which two statements about pattern matching for `instanceof` in Java 17 are correct? (Choose two.)

    1. A. When `o` is declared `Object`, `o instanceof List<String> l` compiles, because the generic type argument is erased before the test is performed.

      Wrong: this states the erasure myth backwards. Erasure is the reason the test cannot be done, so javac rejects the parameterised List pattern as an unsafe cast; the unbounded wildcard List<?> compiles instead.

    2. B. A pattern variable is not implicitly final: it may be reassigned inside the block where it is in scope.Correct answer

      Correct: the compiler declares a pattern variable as a plain local, so it is not implicitly final and may be reassigned inside the block where it is in scope unless you write final.

    3. C. Writing `o instanceof final String s` is a compile-time error, because the `final` modifier is not permitted on a pattern variable.

      Wrong: this inverts the rule. final is explicitly allowed on a pattern variable and is precisely how you make the binding immutable; only then does assigning to it become an error.

    4. D. Matching an expression whose static type is already `String` against the pattern `String t` is a compile-time error.Correct answer

      Correct: a Java 17 type pattern must be able to fail, so matching an expression whose static type is already String against the pattern String t is rejected as an unconditional pattern (Java 21 later lifted this).

    Explanation

    `A pattern variable is not implicitly final...` is correct. The compiler declares the binding as a plain local variable. `Object o = "abc"; if (o instanceof String s) { s = "REASSIGNED"; System.out.println(s); }` compiles and prints `REASSIGNED`. Finality is opt-in, not the default. `Matching an expression whose static type is already `String`...` is correct. A type pattern in `instanceof` must be able to *fail*; a test the compiler already knows the answer to is rejected. With `String s = "java";`, the line `if (s instanceof String t)` is refused with `error: expression type String is a subtype of pattern type String`. (This is a Java 17 restriction, and javac names the version outright: `unconditional patterns in instanceof are not supported in -source 17`. Java 21 lifted it — there the same line compiles.) Why the others are wrong: `Writing `o instanceof final String s` is a compile-time error...` inverts the previous rule. `final` is explicitly allowed on a pattern variable: `if (o instanceof final String s) { System.out.println(s.length()); }` compiles and prints `3` for `"abc"`. Adding `final` is precisely how you *make* the binding immutable — and only then does assigning to it produce `error: cannot assign a value to final variable s`. `When `o` is declared `Object`, `o instanceof List<String> l` compiles...` states the erasure myth backwards. Erasure is the reason the test *cannot* be done: the JVM has no way to check the type argument at run time, so javac rejects any pattern whose cast is not statically safe — `error: Object cannot be safely cast to List<String>`. Use the unbounded wildcard `o instanceof List<?> l`, which is safe and does compile. Exam tip: two mirror-image rules govern which type patterns are legal. The pattern must be *possible* (the cast has to be safe — no `List<String>` from an `Object`, no `String` from an `Integer`) and, in Java 17, it must also be *fallible* (no pattern that always matches). Separately, remember that the binding is a normal mutable local unless you write `final`.

  4. Question 4

    What does this print? ```java public class Main { public static void main(String[] args) { Object o = "abc"; if (o instanceof String s) { s = s.concat("d"); System.out.println(s + " " + o); } } } ```

    1. A. abcd abcCorrect answer

      s is an ordinary local holding a copy of the reference, and s.concat("d") returns a new immutable String rebound to s, leaving o still pointing at the original "abc" - so s prints abcd and o prints abc.

    2. B. The code fails to compile: a pattern variable is implicitly final and cannot be reassigned

      Pattern variables are not implicitly final; this compiles and runs, and only writing instanceof final String s would make reassigning s an error.

    3. C. abc abcd

      Has the two variables backwards, assuming the assignment lands in o and leaves s at its bound value; the assignment rebinds s, and o is never touched.

    4. D. abcd abcd

      Treats the pattern variable as an alias for the matched expression so reassigning s would also change o; s is a separate local, so assigning to it cannot reach back into o.

    Explanation

    Trace: the match succeeds and `s` is initialised with a *copy of the reference* held in `o` — it is an ordinary local variable, not an alias for `o`. `s.concat("d")` returns a brand-new `String` (`String` is immutable, so nothing is mutated in place) and the assignment rebinds `s` to it. `o` still points at the original `"abc"`. So `s` prints as `abcd` and `o` prints as `abc`. Why the others are wrong: `abcd abcd` treats the pattern variable as an alias for the matched expression, so that reassigning `s` would also change `o`. It is a separate local; assigning to it cannot reach back into `o`. `abc abcd` has the two variables backwards — it assumes the assignment lands in `o` and leaves `s` at its bound value. `The code fails to compile: a pattern variable is implicitly final ...` states the most common myth about pattern variables. They are *not* implicitly final: this compiles and runs. (You may write `o instanceof final String s` to make one final — and only then does an assignment become `error: cannot assign a value to final variable s`.) Exam tip: a pattern variable is a plain local variable that the compiler declares and definitely-assigns for you. It is mutable unless you write `final`, and it holds a copy of the reference — reassigning it never changes the thing you matched against. The reverse trap: because `String` is immutable, `s.concat("d")` without the assignment would have printed `abc abc`.

  5. Question 5

    What does this print? ```java public class Main { public static void main(String[] args) { Object o = "abcd"; int n = 0; while (o instanceof String s && s.length() > 1) { n++; o = s.substring(1); } System.out.println(n + " " + o); } } ```

    1. A. The program never terminates: s is bound once, so the condition keeps seeing abcd

      Wrong: this treats the binding as computed once before the loop. The condition re-evaluates o instanceof String s against the freshly assigned o each pass, so the string shrinks and the loop ends.

    2. B. 4 d

      Wrong: this runs one iteration too many by reading the guard as length() >= 1. The loop stops while o is still 'd', because a one-character string fails length() > 1 and is never trimmed to empty.

    3. C. 3 dCorrect answer

      Correct: s is re-bound each iteration and o shrinks abcd to bcd to cd to d; the loop stops when d's length 1 fails > 1, leaving n = 3 and o = d.

    4. D. It fails to compile: the pattern variable s is not in scope inside the body of the while loop

      Wrong: this assumes flow scoping stops at the condition. Because the whole condition must be true for the body to run, the pattern variable s is in scope throughout the body, so o = s.substring(1) is legal.

    Explanation

    Trace: the condition is re-evaluated from scratch on every iteration, so `s` is *re-bound* each time from the current value of `o`. Iteration 1: `o` is `abcd`, length 4 > 1, so `n` becomes 1 and `o` becomes `bcd`. Iteration 2: `bcd`, length 3 > 1, `n` = 2, `o` becomes `cd`. Iteration 3: `cd`, length 2 > 1, `n` = 3, `o` becomes `d`. Iteration 4 test: `d` still matches `String`, but its length is 1, so `1 > 1` is false and the loop exits. `n` is 3 and `o` is `d`, so the line prints `3 d`. Why the others are wrong: `4 d` runs one iteration too many by reading the guard as `length() >= 1` (or by stopping only at the empty string). The loop stops while `o` is still `d` — a one-character string fails `length() > 1`, so it is never trimmed to `""`. `It fails to compile: the pattern variable s is not in scope inside the body ...` assumes flow scoping stops at the condition. It does not: when the whole condition must be true for the body to run, the compiler can prove the match succeeded, so `s` is in scope throughout the body — which is exactly why `o = s.substring(1);` is legal. `The program never terminates: s is bound once ...` treats the binding as computed once before the loop. Each pass re-evaluates `o instanceof String s` against the freshly assigned `o`, so the string really does shrink and the loop really does end. Exam tip: a pattern variable in a `while` condition is in scope in the loop body (the body only runs when the match succeeded) *and* it is re-bound on every test. Reassigning the matched expression inside the body therefore changes what the next iteration binds — the standard idiom for walking a structure. The reverse trap: after the loop, `s` is out of scope, because exiting the loop does not prove the pattern matched.

  6. Question 6

    What does this print? ```java public class Main { public static void main(String[] args) { Object o = Integer.valueOf(41); if (o instanceof Integer i && i > 40 && i % 2 == 1) { System.out.println("odd " + (i + 1)); } else { System.out.println("no match"); } } } ```

    1. A. odd 42Correct answer

      The Integer matches and binds the variable to 41; both numeric guards hold, so the branch prints the label followed by one more than the bound value (flow scoping across &&).

    2. B. no match

      All three conditions hold for 41, so the else branch never runs.

    3. C. Compilation fails: i is not in scope in the conditions after &&

      The right operands of && execute only after the match succeeded, so the binding is definitely matched and in scope throughout the chain.

    4. D. odd 41

      The printed expression adds one to the binding, so the value shown is 42, not the original 41.

    Explanation

    A pattern variable introduced in the first operand of an && chain is in scope in every later operand and in the guarded branch, because each subsequent operand runs only after the match succeeded. Here the integer matches and both numeric guards pass, so the branch prints one more than the bound value. Changing any && to || would break the chain, since a later operand could then run without a successful match.

  7. Question 7

    What does this print? ```java public class Main { static String describe(Object o) { if (!(o instanceof String s)) { return "other"; } return s.toUpperCase(); } public static void main(String[] args) { System.out.println(describe("abc") + " " + describe(7)); } } ```

    1. A. Compilation fails: s is out of scope after the if statement

      Because the non-matching branch always returns, the compiler proves that any code after the if runs only on a successful match, so the binding stays in scope; this is flow scoping, not an error.

    2. B. ABC otherCorrect answer

      The String argument skips the early return and reaches the uppercasing return, while the boxed integer fails the match and takes the early return, so the two results are combined (JLS 17 6.3.2).

    3. C. abc other

      The successful path uppercases the binding, so the lowercase form of the string is never returned.

    4. D. other other

      Only the Integer argument takes the early return; the String argument passes the type test and reaches the uppercasing return.

    Explanation

    When the non-matching branch of an if completes abruptly, such as with a return, the compiler knows any following code executes only after a successful match, so the pattern variable remains in scope past the if. Thus the String argument reaches the uppercasing return, while the boxed integer takes the early exit. Replacing the return with code that completes normally would remove that guarantee and stop the method from compiling.

  8. Question 8

    Given the declaration `Integer n = 5;`, what is the result of compiling and running this statement? `if (n instanceof String s) { System.out.println(s); }`

    1. A. It compiles, and the condition is simply false at runtime

      A runtime-false result occurs only when the types could be related, such as an Object reference; two unrelated class types are rejected before runtime.

    2. B. It fails to compile: no cast from Integer to String exists, and instanceof is rejected whenever the corresponding cast would beCorrect answer

      instanceof applies the cast-compatibility rule, and since Integer and String are unrelated classes with no reference conversion between them, the test is a compile-time error (JLS 17 15.20.2).

    3. C. It compiles but throws ClassCastException at runtime

      instanceof is the safe test and never throws ClassCastException; moreover this code does not even compile.

    4. D. Only the pattern form is rejected; plain `n instanceof String` would compile

      The plain, non-pattern form obeys the same cast-compatibility rule and is rejected identically.

    Explanation

    instanceof is legal only where the corresponding cast would be legal: if the operand's compile-time type cannot be converted to the tested type, the test is a compile-time error rather than a runtime false. Two unrelated class types have no possible reference conversion, so the compiler rejects the test outright. A class-versus-interface test, by contrast, usually compiles, because some subclass could implement the interface.

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

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

Open OCP Java SE 17

Other topics in this pack