skip to content

Reified type parameters cannot do everything. Name concrete limitations of `reified` and explain how you work around them (e.g., needing a real `T` instance or the element type of `T`).

level: seniorimportance: should knowfreq 30%

answer

  1. reified requires inline; not on classes/properties
  2. No T() — use a factory lambda or reflection
  3. is List<String> only checks is List -> use typeOf<T>()
  4. Inlining bloats bytecode; no reflective body
  5. Fall back to Class<T>/KClass<T> for non-inline contexts

basics

~20 s

Reified can't create a T (no T()), can't be used outside inline functions or in classes, and still can't see a type's own generic arguments at runtime. Work around it with a factory lambda, a passed Class<T>/KClass<T>, or typeOf<T>() for nested generics.

solid answer

~50 s

Key limits of `reified`: (1) it requires `inline` — no reified on classes, properties, constructors, or non-inline functions; (2) you cannot instantiate it directly (`T()` is illegal, since there's no guaranteed constructor) — pass a factory `() -> T` or use `T::class.java.getDeclaredConstructor().newInstance()` via reflection; (3) reification still erases the *element* generics of `T` itself — `is List<String>` only checks `is List`; for nested generics capture `typeOf<T>()` instead; (4) inlining bloats bytecode and the reified body can't be invoked reflectively, so very large or hot APIs may prefer a `Class<T>`/`KClass<T>` parameter; (5) a reified function can't be a higher-order *value* you store the way a normal function reference can in all cases, and you can't recursively call it with a non-reified type variable. Workarounds map directly: factory lambdas for construction, explicit `Class`/`KClass` parameters for non-inline or reflective contexts, `typeOf<T>()` for generic-argument awareness, and crossinline/noinline tuning for the inline cost.

code

kotlin · 9 lines
kotlin
// Cannot do: inline fun <reified T> create(): T = T()  // illegal

// Work around construction with a factory:
inline fun <reified T> create(factory: () -> T): T = factory()

// Work around nested-generic erasure with typeOf:
import kotlin.reflect.typeOf
inline fun <reified T> elementArgs() = typeOf<T>().arguments
// elementArgs<Map<String, Int>>() -> [String, Int]

go deeper

for a junior

Knows you can't do T() and that reified needs inline.

for a middle

Lists several limits and the basic factory-lambda workaround.

for a senior

Maps each limitation to the right workaround (factory, Class/KClass, typeOf) and explains the inlining cost.

for a principal

Designs library APIs choosing reified vs Class<T> deliberately, reasons about bytecode size, reflective access, and composition across module boundaries.

## What reified can and can't do Reification works by **inlining**: the compiler copies the function body to the call site and substitutes the concrete type. That mechanism creates hard limits. ### 1. Requires `inline` `reified` is only legal on type parameters of `inline` functions. You cannot have a reified type parameter on a **class**, **interface**, **property**, or **non-inline function**. A class has one runtime form, so its type args stay erased. ### 2. You cannot construct `T` ```kotlin inline fun <reified T> make(): T = T() // ERROR: no way to know T has a constructor ``` Workarounds: ```kotlin // (a) pass a factory inline fun <reified T> make(factory: () -> T): T = factory() make { User() } // (b) reflection (slower, can throw) inline fun <reified T : Any> makeReflect(): T = T::class.java.getDeclaredConstructor().newInstance() ``` ### 3. Element generics of `T` are still erased Reification gives you the **class**, not its type arguments. `is List<String>` collapses to `is List`. If you need the element type, capture it with `typeOf<T>()`: ```kotlin import kotlin.reflect.typeOf inline fun <reified T> elementType() = typeOf<T>().arguments // e.g. [String] for List<String> ``` This is exactly what serialization libraries do. ### 4. Inlining cost & no reflective body Every call site gets a copy of the body, so heavy reified functions inflate bytecode. The function has **no standalone body** to call reflectively. For large/public APIs that don't need call-site types, prefer threading a `Class<T>` or `KClass<T>`: ```kotlin fun <T : Any> load(type: KClass<T>): T = ... ``` ### 5. Composition limits A reified `T` is fixed at the call site. You cannot forward it into another reified call using a *non-reified* type variable, and storing the function as a plain reference loses the per-call type. To keep inlining benefits while limiting cost, mark heavy lambda params `crossinline`/`noinline` as appropriate. ## Decision guide - Need to **construct** T -> factory lambda (or reflection if you must). - Need **non-inline**/stored/reflective use -> pass `Class<T>`/`KClass<T>`. - Need **nested generic** info -> `typeOf<T>()`. - Just need `is`/`as`/`T::class` at the call site -> `reified` is perfect.

  • If you can't put reified on a class, how do generic-aware libraries (like a typed repository) keep the type around?
    They accept the type as a constructor/factory argument — typically a `KClass<T>`/`Class<T>` or a captured `typeOf<T>()` — passed in from a reified inline factory function at the call site, e.g. `inline fun <reified T : Any> repo() = Repository(T::class)`.
  • Why might you prefer a `Class<T>` parameter over reified even when reified would compile?
    To avoid bytecode bloat from inlining at many call sites, to allow non-inline/reflective use, to keep the function callable as a stored reference, and for cleaner Java interop.

saying these in an interview costs you the question

  • Claiming you can write `T()` to instantiate a reified type
  • Saying reified recovers element generics like List<String>
  • Thinking reified can be put on a class
  • Ignoring the bytecode-bloat cost of inlining
  • Believing a reified function can be invoked via reflection

context