When does an instanceof expression fail to compile rather than simply evaluate to false?
answer
- Provably-impossible test → compile error, not false
- Reasoned from the LEFT operand's declared type
- String vs Integer → 'inconvertible types' error
- Object vs Integer → compiles, returns false
- No parameterized generics (List<String>); raw/`<?>` ok
basics
~20 sIf the compiler can prove the test could never possibly be true — because the two types have no possible relationship — instanceof is a compile error, not a false result. It also won't compile with a primitive on the left.
solid answer
~50 sinstanceof is a compile error (not a runtime `false`) when the compiler can statically prove the test is impossible. For two class types, if neither is a subtype of the other and the left operand's declared type can never hold an instance of the right type, you get 'inconvertible types'. For example, `String s = ...; if (s instanceof Integer)` won't compile, because no object can be both a String and an Integer. The check is based on the **declared (static) type** of the left operand. With interfaces it's more lenient: testing a concrete class reference against an unrelated interface usually compiles, because a subclass could implement that interface. Other compile failures: a primitive left operand (`5 instanceof Integer`), or a parameterized generic right operand like `List<String>` (not reifiable); the raw `List` or `List<?>` is allowed. With pattern matching, redeclaring an already-in-scope variable name also fails to compile.
go deeper
Know that some instanceof tests don't compile, and that you can't use primitives on the left.
Explain that a provably-impossible test (unrelated classes) is a compile error, illustrated by String vs Integer.
Articulate that the check uses the declared type, the class-vs-interface leniency, and the generics/erasure restriction (no parameterized types).
Discuss how widening to Object subverts the static check, and why this nudges designs toward sealed types/polymorphism rather than instanceof chains.
## The principle: provably-impossible tests are errors For most operators, a meaningless comparison just yields `false`. `instanceof` is stricter: if the **compiler can prove at compile time** that the test could *never* be `true`, it refuses to compile the program (error message: *"inconvertible types"*). The rationale is that such a test is almost certainly a programming mistake, so the language surfaces it early rather than silently returning `false`. ## It uses the declared (static) type of the left operand The compiler reasons from the **declared type** of the left-hand reference, not whatever it might point to at run time. Given: ```java String s = "hi"; if (s instanceof Integer) { ... } // COMPILE ERROR ``` The declared type of `s` is `String`. `String` and `Integer` are unrelated class hierarchies — no value can be both — so the compiler proves the test impossible and errors. Contrast: ```java Object o = "hi"; if (o instanceof Integer) { ... } // compiles; evaluates to false ``` Now the declared type is `Object`, which *could* hold an `Integer`, so the test is plausible at compile time and merely returns `false` at run time. ## Classes vs interfaces - **Class vs unrelated class**: error (neither is a subtype of the other and neither could be). - **Class vs interface**: usually **allowed**, because a *subclass* of that class could implement the interface — unless the left class is `final` and does not implement it, in which case the compiler can prove impossibility and errors. Example that compiles: ```java Object obj = ...; if (obj instanceof Runnable) { ... } // fine; could be a Runnable ``` ## Other compile-time failures 1. **Primitive left operand**: `int i = 5; if (i instanceof Integer)` — error. instanceof requires a reference type on the left. 2. **Parameterized (non-reifiable) generic types on the right**: `if (x instanceof List<String>)` — error, because generic type arguments are erased at run time and can't be checked. You may write the **raw** or **unbounded wildcard** form: `x instanceof List` or `x instanceof List<?>`. 3. **Pattern-variable name clash**: with pattern matching, `if (obj instanceof String s)` where `s` is already a variable in scope is a compile error (duplicate variable). ## Why this matters in practice Understanding this distinguishes a *false* result (the types *could* relate but this object doesn't match) from a *compile error* (the types can *never* relate). It also explains why widening the declared type to `Object` changes a compile error into a legal-but-false test — occasionally a deliberate technique, but usually a sign the design should use polymorphism or sealed types instead. ## Defining the key terms - **Static / declared type**: the type the compiler sees on a variable's declaration. - **Reifiable type**: a type whose full information exists at run time. `List` is reifiable; `List<String>` is not (generics are erased), which is why the latter can't be tested with instanceof. - **Erasure**: the process by which the compiler removes generic type parameters, leaving only raw types at run time.
- Why does `Object o = "hi"; o instanceof Integer` compile but `String s = "hi"; s instanceof Integer` not?instanceof reasons from the declared type. Object could legitimately hold an Integer, so the test is plausible (returns false at runtime). String can never be an Integer, so the compiler proves it impossible and reports 'inconvertible types'.
- Can you write `x instanceof List<String>`?No. Generic type arguments are erased at runtime, so they can't be tested. You may use the raw `x instanceof List` or the unbounded wildcard `x instanceof List<?>`.
saying these in an interview costs you the question
- Assuming an impossible instanceof just returns false instead of failing to compile
- Thinking the check uses the runtime type rather than the declared type
- Believing `x instanceof List<String>` is allowed
- Forgetting that widening the left declared type to Object turns the error into a legal false test