How does instanceof pattern matching behave with null, and what are common pitfalls when relying on it as a null check?
answer
- null instanceof T → always false
- Bound variable always non-null in scope
- Negative branch = null OR wrong type
- switch patterns still NPE without case null
- Don't add redundant null check on the binding
basics
~20 snull instanceof Type t is always false, so the pattern variable is never bound for null — instanceof gives you a free null check. The pitfall: switch pattern matching changed null handling, and don't assume the variable is non-null without the test passing.
solid answer
~50 sFor `instanceof`, null never matches any type: `null instanceof String s` is false and `s` is not bound. This means an instanceof pattern is implicitly null-safe — if `obj instanceof String s` is true, `s` is guaranteed non-null inside the matched region, so you never get a NullPointerException from using it there. A common pitfall is carrying intuition from this into `switch` pattern matching, where the rules differ: a traditional `switch` throws NPE on a null selector, but a switch with patterns can include an explicit `case null` (and you can write `case null, default`), so null behavior is opt-in and easy to get wrong. Another pitfall is assuming the negative branch means 'obj is null' — it actually means 'obj is null OR not a String'. So `if (!(obj instanceof String s))` covers both null and wrong-type cases, which is usually what you want but worth stating precisely.
go deeper
Knows null instanceof Type is false and that the bound variable is never null.
Explains the free null-guard, that the negative branch means null-or-wrong-type, and that the variable needs no null check.
Contrasts instanceof null behavior with switch pattern matching's NPE-unless-case-null rule and reasons about distinguishing null from wrong type.
Sets conventions around null handling across instanceof and switch patterns, especially for sealed hierarchies, and avoids surprising NPEs in dispatch code.
## Null and instanceof: the core rule In Java, `null instanceof AnyType` is **always false** — and this has been true since long before pattern matching. The pattern form inherits this exactly: ```java String obj = null; if (obj instanceof String s) { // false, because obj is null // never entered; s is never bound } ``` Because the variable `s` is only bound when the test is true, and the test is false for null, **`s` is guaranteed non-null** wherever it's in scope. So an instanceof pattern is effectively a combined **null check + type check + cast**: ```java if (obj instanceof String s && s.length() > 0) { ... } // reaching s.length() means obj was non-null AND a String ``` This is genuinely useful: it replaces the older three-line `if (obj != null && obj instanceof String) { String s = (String) obj; ... }` with one expression (the explicit null check was always redundant, but the pattern makes the safety obvious). ## Pitfall 1: the negative branch covers TWO cases ```java if (!(obj instanceof String s)) { // obj is null OR obj is not a String } ``` Don't read the false branch as 'obj is null'. It is 'null **or** wrong type'. Usually that's exactly the guard you want, but if you need to distinguish null from a wrong-type object, you must test null separately. ## Pitfall 2: carrying the rule into switch pattern matching This is the big one. A *traditional* `switch` (on an enum or String) **throws NullPointerException** if the selector is null. **Switch pattern matching** (a separate feature) changes the landscape: ```java String s = ...; switch (s) { case null -> System.out.println("was null"); // explicit null case allowed case "x" -> ...; default -> ...; } ``` - Without a `case null`, a pattern switch still **throws NPE** on null (preserving compatibility). - With `case null` (or the combined `case null, default`), null is handled explicitly. The pitfall is assuming "patterns are null-safe like instanceof" and forgetting that a pattern *switch* still NPEs on null unless you add `case null`. The null-safety is an `instanceof`-operator property, not a blanket pattern-matching property. ## Pitfall 3: assuming the bound variable could be null The opposite mistake: adding a redundant `if (s != null)` inside the matched region. Since the match excludes null, that check is dead code and signals a misunderstanding. ## Summary - `instanceof` pattern: null never matches → variable is always non-null in scope → free null guard. - Negative branch = null OR wrong type. - `switch` patterns: still NPE on null unless you write `case null`. ## Terms recap - **NullPointerException (NPE)**: thrown when dereferencing null. - **selector**: the value a `switch` evaluates. - **case null**: explicit null branch allowed only in pattern (and some recent) switches. - **null guard**: code preventing use of a null value.
- Does a pattern switch over a sealed interface throw if the selector is null?Yes, unless it has a `case null` (or `case null, default`). Pattern switches preserve the traditional NPE-on-null behavior by default; null handling is opt-in.
- How would you distinguish a null obj from a wrong-type obj?Test null first: `if (obj == null) { ... } else if (obj instanceof String s) { ... } else { /* wrong type */ }`. The instanceof negative branch alone collapses null and wrong-type into one path.
saying these in an interview costs you the question
- Claiming null matches instanceof patterns
- Reading the false branch as 'obj is null' only
- Assuming switch patterns are automatically null-safe
- Adding a redundant null check on a bound pattern variable