skip to content

What does it mean for a type to be 'reifiable' in Java, and why does the concept exist?

level: middleimportance: must knowfreq 55%

answer

  1. Reify = make real at runtime
  2. Erasure throws away type arguments
  3. Reifiable: primitives, non-generic, raw, <?> unbounded, arrays of reifiable
  4. Non-reifiable: List<String>, bounded wildcards, T
  5. Gates instanceof, array creation, checked casts

basics

~20 s

A reifiable type is one whose full type information is still available at runtime. Because Java generics are erased (forgotten) at compile time, types like List<String> are NOT reifiable, while String, int, and List (the raw type) are.

solid answer

~40 s

A type is reifiable when its type information is fully present at runtime. Java implements generics by type erasure: the compiler checks generic types, then removes the type parameters so the JVM never sees them. That makes most parameterized types like List<String> non-reifiable, because at runtime there is just List. Reifiable types are those that don't lose anything to erasure: primitives (int), non-generic classes/interfaces (String, Runnable), raw types (List), parameterizations where every argument is an unbounded wildcard (List<?>, Map<?,?>), and arrays whose component type is itself reifiable (String[], List<?>[]). The concept exists because some language features only work when the type is fully known at runtime: creating arrays, instanceof checks, and casts that the JVM can actually verify. So 'reifiable' is the dividing line the spec uses to decide which generic operations are allowed.

go deeper

for a junior

Can state that generics are erased and that runtime doesn't know the <String> part; recognizes List<String> isn't 'fully real' at runtime.

for a middle

Defines reifiable precisely and lists the categories (primitive, non-generic, raw, unbounded wildcard, array of reifiable) and contrasts with non-reifiable.

for a senior

Connects reifiability to the concrete restrictions it gates (instanceof, array creation, unchecked casts) and explains why unbounded wildcards are reifiable.

for a principal

Can reason about the compatibility rationale for erasure vs reification, trade-offs versus reified generics (C#/.NET), and how this shapes API/library design decisions.

## The core problem: erasure When Java added generics in Java 5, it had to stay compatible with older bytecode and the existing JVM. The chosen mechanism is **type erasure**: the compiler uses the type arguments (`<String>`) to check your code, then *erases* them. After compilation, `List<String>` and `List<Integer>` are both simply `List` at the bytecode/runtime level. The type parameter `T` is replaced by its bound (`Object` for an unbounded `T`, or the upper bound like `Number` for `T extends Number`). So at **runtime**, the JVM has no idea a particular `List` was declared to hold `String`. That information was thrown away. ## What 'reifiable' means **Reify** means 'to make real/concrete'. A **reifiable type** is a type whose complete type information is *still present and available at runtime* — nothing was lost to erasure. Equivalently: the runtime representation of the type is exactly the same as the source-code type. A **non-reifiable type** is one that loses information to erasure, so its runtime form is less specific than what you wrote. ## The list of reifiable types (per the Java Language Specification) A type is reifiable if it is one of: 1. A **primitive** type — `int`, `double`, `boolean`, etc. (no generics involved). 2. A **non-generic** class or interface type — `String`, `Number`, `Runnable`. 3. A **raw type** — `List`, `Map`, `ArrayList` (generic class used with no type arguments at all). 4. A parameterized type where **all** type arguments are **unbounded wildcards** — `List<?>`, `Map<?,?>`, `Collection<?>`. There is nothing specific to lose: `?` already means 'some unknown type', which is what runtime knows anyway. 5. An **array** whose component type is reifiable — `int[]`, `String[]`, `List<?>[]`, `Number[][]`. ## The list of non-reifiable types Everything else with 'real' type arguments: - `List<String>`, `ArrayList<Integer>`, `Map<String,User>` — concrete arguments are erased. - `List<? extends Number>`, `List<? super Integer>` — *bounded* wildcards still carry information that is lost. - A bare type variable like `T` (it's not a concrete type either). ## Why this distinction matters The JLS uses 'reifiable' as the gate for operations that need real runtime type info: - **`instanceof`** can only test against reifiable types: `x instanceof List<?>` is legal, `x instanceof List<String>` is a compile error — at runtime there's no way to check the `String`. - **Array creation** with `new` requires a reifiable component type: `new List<String>[10]` is illegal (generic array creation error); `new List<?>[10]` is allowed. - **Casts** to non-reifiable types compile only with an 'unchecked cast' warning, because the JVM can't fully verify them. ## Mental model Think of erasure as the compiler taking off the generic 'labels' before handing the object to the JVM. Reifiable types are the ones with no labels to lose (or whose only label is the catch-all `?`). Everything that had a meaningful label removed is non-reifiable, and the language forbids exactly those operations that would need the missing label.

  • Is List<?> reifiable? Why or why not?
    Yes. An unbounded wildcard means 'some unknown type argument', which is exactly all the runtime knows after erasure — nothing specific is lost, so List<?> is reifiable.
  • What does a type variable like T erase to?
    To its leftmost bound: Object for an unbounded T, or the upper bound (e.g. Number) for T extends Number. T itself is not reifiable.

saying these in an interview costs you the question

  • Saying List<String> is reifiable because you can see <String> in the source — runtime is what matters
  • Claiming List<?> is non-reifiable (it IS reifiable — unbounded wildcard loses nothing)
  • Confusing erasure with reflection: getClass() on a List<String> returns List.class, proving erasure
  • Thinking primitives are 'not generic so the question doesn't apply' — they are explicitly reifiable

context