skip to content

Kotlin advertises 'reified generics' for inline functions, even though the JVM erases generics. At the model level, what does reified give you and what are its boundaries?

level: seniorimportance: should knowfreq 50%

answer

  1. JVM erases generics; reified brings the type back
  2. reified requires inline (body copied to call site)
  3. Enables is T / as T / T::class / typeOf<T>()
  4. filterIsInstance is the canonical example
  5. No function refs; caller must know concrete type

basics

~20 s

Normally a generic type is erased, so you cannot check it at runtime. Marking an inline function's type parameter reified makes the actual type available inside the function, so you can write checks like 'is T' without passing a Class object.

solid answer

~50 s

On the JVM, generic type arguments are **erased** at runtime, so ordinarily you cannot do `x is T` or `T::class` inside a generic function. Kotlin's workaround is **`reified`** type parameters, allowed only on **`inline`** functions. Because an inline function's body is copied into each call site, the compiler substitutes the concrete type argument there, making it physically present. Inside the body you can then call `x is T`, `x as T`, and access `T::class` / `typeOf<T>()`. The classic example is `inline fun <reified T> Gson.fromJson(json): T` or `filterIsInstance<T>()`. Boundaries: it works only because of inlining, so you cannot take a function reference to a reified function, cannot use it from non-inline contexts, and the call site must know the concrete type (you cannot bridge a plain runtime `Class<*>`). It does not defeat erasure globally — it only reconstructs the type at each inlined call.

code

kotlin · 4 lines
kotlin
inline fun <reified T> parse(raw: String): T =
    objectMapper.readValue(raw, T::class.java)

val user: User = parse(jsonString)  // no Class token needed at the call site

go deeper

for a junior

Knows reified lets you use the type inside the function (e.g. is T) without a Class parameter.

for a middle

Explains the inline requirement and gives a real use case like JSON parsing or filterIsInstance.

for a senior

Articulates how inlining enables substitution, the limits (no refs, caller must know type), and typeOf<T>() for nested types.

for a principal

Weighs reified API ergonomics against bytecode bloat and interop, and designs erasure-crossing APIs (serialization, DI) accordingly.

## Erasure: the starting point The JVM does not keep generic type arguments at runtime — a `List<String>` and a `List<Int>` are both just `List` once compiled. This is **type erasure**. As a consequence, ordinary code cannot ask questions like `value is T` or get `T::class`, because by runtime `T` is gone. (In Java you work around this by passing a `Class<T>` token.) ## What `reified` does Kotlin lets you mark a type parameter `reified`, but **only on an `inline` function**: ```kotlin inline fun <reified T> Any?.isOfType(): Boolean = this is T println("hi".isOfType<String>()) // true println(42.isOfType<String>()) // false ``` An `inline` function's body is **copied into every call site** by the compiler. At each site the actual type argument is known, so the compiler can substitute it directly into the copied body. That makes the concrete type physically present where `is T` / `as T` / `T::class` appear — effectively *reifying* it. ## What you can do inside a reified function - `x is T` and `x !is T` runtime checks. - `x as T` casts (still unchecked for the generic part of nested types). - `T::class` to get the `KClass`, and `typeOf<T>()` to get a full `KType` (which can even capture nested arguments like `List<String>`). ```kotlin inline fun <reified T> Iterable<*>.onlyOfType(): List<T> = filterIsInstance<T>() ``` `filterIsInstance` in the standard library is itself a reified inline function. ## Boundaries and limits - **Inline-only.** You cannot mark a normal (non-inline) function's parameter `reified`. There is nowhere to substitute the type. - **No function references.** Because it must be inlined, you cannot take a `::ref` to a reified function or store it as a lambda value the usual way. - **Caller must know the type.** The concrete type argument has to be statically known at the call site. You cannot turn a runtime `Class<*>` you received as a parameter into a reified `T`. - **Not global.** It does not remove erasure from the program; it only reconstructs the type within each inlined expansion. Other code still sees erased generics. - **Cost.** Inlining duplicates the body, so heavy reified utilities can grow bytecode. ## Why it belongs in the type model Reification is Kotlin's pragmatic bridge across the JVM's erasure boundary: instead of changing the runtime, it leverages inlining so that, *at the places that matter*, the type the developer wrote is available — giving ergonomic `fromJson<User>()`-style APIs without `Class<T>` tokens.

  • Why can't a normal (non-inline) function have a reified type parameter?
    Reification works by substituting the concrete type into the function body that is inlined at each call site; a non-inline function has a single compiled body where the type is already erased, so there is nowhere to substitute it.
  • Does reified let you recover the type argument of a value you received as List<*>?
    No. Reified only reconstructs the type written at the call site of the reified function; an already-erased List<*> value gives you no element type to recover.

Inlining is photocopying the recipe into each kitchen; reified writes the actual ingredient name on every copy so each cook can check it.

saying these in an interview costs you the question

  • Saying reified removes erasure everywhere on the JVM
  • Forgetting that reified requires inline
  • Claiming you can pass a runtime Class<*> in as the reified T
  • Thinking you can take a function reference to a reified function
  • Confusing T::class (KClass) with T::class.java (Java Class) carelessly

context