skip to content

What instanceof and cast operations are restricted by reifiability, and why?

level: middleimportance: should knowfreq 45%

answer

  1. instanceof needs reifiable type
  2. x instanceof List<String> = compile error
  3. Use instanceof List<?> or raw List
  4. Cast to List<String> = unchecked warning
  5. Heap pollution -> ClassCastException later

basics

~20 s

You can only use instanceof with reifiable types. 'x instanceof List<String>' is a compile error because the <String> is erased and can't be checked at runtime; use 'x instanceof List<?>' or raw 'List' instead. Casts to List<String> compile but give an 'unchecked' warning.

solid answer

~50 s

`instanceof` performs a runtime type test, so it only works on types the runtime fully knows — reifiable types. `x instanceof List<String>` is rejected at compile time because after erasure there is no `String` info to test; the legal forms are `x instanceof List<?>` or `x instanceof List` (raw), both reifiable. Casts behave differently: a cast *to* a non-reifiable type like `(List<String>) obj` does compile, but the compiler can only verify the erased part (`List`), so it emits an **unchecked cast** warning — at runtime it's effectively a cast to `List`, and a wrong element won't be caught until you pull it out and it fails with `ClassCastException` (heap pollution). So reifiability gates `instanceof` outright, and downgrades non-reifiable casts to unchecked. Best practice: test/cast against `List<?>` when you only need 'is it a list', and avoid relying on unchecked casts.

code

java · 11 lines
java
Object x = new ArrayList<Integer>();

// if (x instanceof List<String>) {}   // COMPILE ERROR: non-reifiable
if (x instanceof List<?> list) {        // OK: reifiable wildcard form
    System.out.println(list.size());
}

@SuppressWarnings("unchecked")
List<String> ls = (List<String>) x;     // compiles with unchecked warning
// Runtime check only confirms it's a List, not a List<String>:
String s = ls.get(0);                   // ClassCastException (heap pollution)

go deeper

for a junior

Knows instanceof List<String> doesn't compile and that you'd write instanceof List<?> instead.

for a middle

Explains erasure as the reason, knows the legal instanceof forms, and that casts to List<String> give an unchecked warning.

for a senior

Explains heap pollution and the deferred ClassCastException, when an unchecked cast is acceptable, and how to scope @SuppressWarnings.

for a principal

Frames the runtime-verifiability invariant across instanceof/cast/array-store, and sets team policy on unchecked warnings, safe-cast utilities, and avoiding raw types.

## What instanceof and cast actually do at runtime - `x instanceof SomeType` asks the JVM, at runtime, 'is the object referenced by `x` an instance of `SomeType`?' It returns a boolean. - `(SomeType) x` is a *checked cast*: the JVM verifies at runtime that `x` really is a `SomeType`, throwing `ClassCastException` if not. Both need **real runtime type information**. After **type erasure** (the compiler removes generic type arguments), the only generic info left is the raw class. So these operations can only fully work on **reifiable** types. ## instanceof: restricted to reifiable types The JLS forbids `instanceof` against a non-reifiable type: ```java if (x instanceof List<String>) { ... } // COMPILE ERROR ``` There is no `String` at runtime to test, so the check is meaningless — the compiler rejects it rather than let you write a test that can't mean what it says. Legal forms: ```java if (x instanceof List<?>) { ... } // OK: List<?> is reifiable if (x instanceof List) { ... } // OK (raw), but raw-type warning ``` With pattern matching, `x instanceof List<?> list` is the idiomatic modern form. Note: `x instanceof List<?>` only tells you 'it's some kind of List' — it cannot tell you the element type. ## Casts: non-reifiable casts compile but are 'unchecked' Casting *to* a non-reifiable type is allowed but only partially verifiable: ```java Object o = new ArrayList<Integer>(); List<String> ls = (List<String>) o; // compiles, 'unchecked cast' WARNING ``` At runtime the JVM can only check the erased part — that `o` is a `List`. It cannot check that it's a `List<String>`. So the cast 'succeeds' even though the list actually holds `Integer`s. This is **heap pollution**: a variable of type `List<String>` now references a list with the wrong elements. The error surfaces later, away from the cast: ```java String s = ls.get(0); // ClassCastException here, not at the cast ``` The **unchecked warning** is the compiler telling you: 'I verified what I could; the rest is on you.' ## Why downcasting a generic collection is dangerous A `(List<String>) someList` cast is exactly this unchecked situation — there's no way to confirm the elements. Prefer designs that don't need it; if you must, isolate it and document why it's safe (e.g. you created the list yourself). ## Reifiable casts are fully checked Casting to a reifiable type is fully verified, no warning: ```java Object o = ...; List<?> anyList = (List<?>) o; // fully checked, no warning String str = (String) o; // fully checked ``` ## Summary table | Operation | Reifiable target | Non-reifiable target | |---|---|---| | `instanceof` | allowed | compile error | | cast | fully checked, no warning | compiles, unchecked warning (partial check) | The unifying rule: anything that needs to *verify a type at runtime* can only fully do so for reifiable types, because erasure removed everything else.

  • What is 'heap pollution'?
    When a variable of a parameterized type (e.g. List<String>) refers to an object that isn't of that type (e.g. a list of Integers), usually via an unchecked cast or generic varargs. It causes ClassCastException later when an element is retrieved.
  • Does (List<String>) obj throw at the cast site if obj is a List<Integer>?
    No. The runtime only checks the erased type (List), which matches, so the cast succeeds. The ClassCastException happens later when you read an element and it's not a String.

saying these in an interview costs you the question

  • Thinking (List<String>) cast is verified at runtime — only the erased List part is
  • Believing x instanceof List<?> tells you the element type — it doesn't
  • Saying instanceof List<String> just returns false — it doesn't compile at all
  • Treating an unchecked-cast warning as harmless noise to blanket-suppress

context