Pattern Variable Scope in Java
Pattern variables are available only in regions where Java can prove that their pattern has successfully matched. This flow-sensitive rule lets pattern matching combine type testing and a typed variable without unsafe casts.
A type pattern such as `obj instanceof String s` both tests the runtime type and introduces `s`. The variable is not automatically available everywhere after the `instanceof`; its scope depends on the control-flow conditions that guarantee the match succeeded.
For a simple positive test, the variable can be used inside the `if` body: `if (obj instanceof String s) { ... }`. Java also allows it after a negated test when control flow guarantees the pattern matched, such as `if (!(obj instanceof String s)) return; use(s);`.
Boolean operators affect scope. With `&&`, the right-hand side is evaluated only when the left side is true, so a pattern variable established on the left can be used on the right: `obj instanceof String s && s.length() > 0`. With `||`, the right side is not guaranteed to run only after a successful match, so the variable generally cannot be used there.
The key idea is flow scoping rather than ordinary lexical scope alone. The compiler tracks whether a successful pattern match is guaranteed along a particular path; if a path could reach a use without the match having succeeded, the variable is out of scope at that point.
Key points
- Pattern variables have flow-sensitive scope tied to a successful pattern match.
- An `if` body after a positive `instanceof` test can use the pattern variable.
- Negated tests can make a pattern variable available after an early exit.
- `&&` can propagate pattern-variable availability to its right operand; `||` generally cannot.
- Pattern-variable scope is determined by compile-time control-flow analysis, not just braces.
Interview questions
What is a pattern variable in Java?
A pattern variable is a variable introduced by a pattern-matching construct such as `obj instanceof String s`. If the pattern matches, `s` refers to the value with the matched type. Its availability is flow-sensitive, so the compiler only permits uses where the match is known to have succeeded. This avoids writing a separate cast after a successful type check.
Why can a pattern variable be used after `if (!(x instanceof String s)) return;`?
The condition returns whenever the pattern does not match. Therefore, reaching the code after the `if` means the negated condition was false, which proves that the pattern matched. Java's flow analysis uses that fact to make `s` available afterward. The rule is based on reachable control-flow paths rather than simply on the textual location of the declaration.
Why does `&&` allow a pattern variable on its right-hand side?
For `x instanceof String s && s.length() > 0`, Java evaluates the right operand only if the left operand is true. A true left operand establishes that the pattern matched and therefore that `s` exists with the matched type. The compiler can consequently make `s` available in the right operand. This is an example of flow scoping driven by short-circuit evaluation.
Why can't you generally use a pattern variable after `if (x instanceof String s || ...)`?
The right side of `||` may be evaluated even when the left side is false. If the left pattern is false, `s` was never established, so the compiler cannot treat it as definitely available across that expression. Pattern-variable scope therefore follows paths on which the match is guaranteed, rather than merely paths where the expression as a whole may become true. This prevents uses of an unestablished binding.
What does 'flow scoping' mean for pattern variables?
Flow scoping means the compiler determines a pattern variable's usable region from control-flow facts about whether the corresponding pattern has definitely matched. This region can cross an apparent lexical boundary, such as after an early return, because the compiler can eliminate paths where the match failed. Conversely, a variable can be unavailable inside a syntactically nearby expression if that expression can be reached without a successful match. The model is therefore based on proven execution paths rather than a simple textual block.