skip to content

Why can't you put `reified` on the type parameter of a regular class, a constructor, or a non-inline function, and what is the standard workaround when you genuinely need the type inside one of those?

level: middleimportance: must knowfreq 50%

answer

  1. Reified = compile-time inlining substitution
  2. Class/constructor compiled once -> can't specialize
  3. Workaround: store KClass/Class/KType token
  4. Reified factory + non-inline class is the idiom
  5. Cross a non-inline boundary -> convert T to a token

basics

~20 s

Reified only works on inline functions because the compiler copies the function body and bakes the real type in. Classes and normal functions exist once at runtime, so there's nothing to bake into. The fix: pass a Class<T>/KClass<T> token instead.

solid answer

~50 s

`reified` works by **inlining**: the compiler copies the inline function's body into every call site and substitutes the concrete type for the type parameter, so `T::class` becomes a literal. That mechanism requires a fresh copy per call. A class, a constructor, or a non-inline function is compiled **once** and shared by all instantiations, so there's no per-call site to specialize and the type argument stays erased — hence `reified` is rejected there by the compiler. The standard workaround is the explicit **type token**: store a `KClass<T>` / `Class<T>` (or a `KType`) as a constructor parameter / field and use it wherever you'd have used `T::class`. A common pattern is an abstract base class whose constructor takes the token, plus an `inline reified` factory that captures it: `inline fun <reified T> create() = Holder(T::class)`. This combines reified convenience at the call site with a non-reified, single-compiled class underneath.

code

kotlin · 10 lines
kotlin
import kotlin.reflect.KClass

abstract class Codec<T : Any>(protected val type: KClass<T>) {
    fun decode(bytes: ByteArray): T = mapper.readValue(bytes, type.java)
}

class UserCodec : Codec<User>(User::class)

// Reified convenience that feeds the non-inline base its token
inline fun <reified T : Any> codec(): Codec<T> = object : Codec<T>(T::class) {}

go deeper

for a junior

Knows reified is only for inline functions, not classes.

for a middle

Explains the inlining/specialization mechanism and applies the token workaround in a class.

for a senior

Designs the reified-factory + non-inline-core idiom and knows when to store KType vs KClass.

for a principal

Weighs API tradeoffs of token-carrying generics vs. codegen/serialization frameworks across module boundaries.

## How reified actually works `reified` is not magic type metadata — it is a **compile-time substitution** enabled by inlining. When you mark an `inline fun <reified T>`, the compiler pastes the function body into each call site and replaces every use of `T` (`T::class`, `is T`, `as T`, `typeOf<T>()`) with the concrete type used there. Because each call site gets its own specialized copy, the type is known. ## Why classes and non-inline functions can't have it - A **class** is compiled to a single bytecode definition shared by `Box<String>` and `Box<Int>` alike. There is no per-instantiation copy to specialize, so a class type parameter is always erased — `reified` on a class type parameter is a compile error. - A **constructor** can't be inline, for the same reason. - A **non-inline function** has one shared body; nothing gets pasted/specialized, so `reified` is illegal. ## The workaround: carry a token When you need T inside such a declaration, **pass the type explicitly**: ```kotlin class TypedRepository<T : Any>(private val type: KClass<T>) { fun findAll(): List<T> = db.query(type.java) // use token instead of T::class } // Reified factory hides the token from callers: inline fun <reified T : Any> typedRepository() = TypedRepository(T::class) val repo = typedRepository<User>() ``` For nested generics, store a `KType` from `typeOf` (captured by a reified factory) instead of a `KClass`. ## The combined idiom The canonical recipe: a non-inline class/function takes a `Class`/`KClass`/`KType`; a thin `inline reified` wrapper supplies it. Jackson (`readValue(json, Class)` + `inline reified readValue`) and Spring follow exactly this shape. ## Gotcha Inside an `inline reified` function you can call other inline reified functions and pass `T` along, but the moment you cross into a non-inline boundary (a lambda that isn't `crossinline`-friendly in this respect, a stored class, a constructor) you must convert `T` into a token value.

  • Can an interface or abstract class declare a reified type parameter?
    No. reified is only valid on inline function type parameters; interfaces, classes, and abstract members are compiled once and can't specialize, so they must carry a token.
  • Inside an inline reified function, why might you still need to pass T::class into something?
    Because as soon as T must live past the inlined body — stored in a field, captured by a non-inline class, or used in a constructor — there's no specialized copy there, so you materialize T as a KClass/Class token.

Reified is a stencil pressed fresh at each call site; a class is a single mass-produced mold, so you instead slip a labeled tag (the token) inside it.

saying these in an interview costs you the question

  • Saying reified works on classes if you 'just add the keyword'
  • Believing reified stores runtime type metadata on the instance
  • Not knowing constructors can't be inline/reified
  • Forgetting the token workaround entirely
  • Claiming you can call typeOf<T>() inside a non-inline method

context