At runtime, what happens to a generic function's type parameter T, and how does that limit what you can do with T inside the body?
answer
- Default generics are erased — T gone at runtime
- Forbidden: x is T, T(), T::class
- as T compiles but is UNCHECKED (CCE later)
- Fix: inline fun <reified T> makes T real
- Alternative: pass KClass<T>/Class<T> token
basics
~20 sBy default the type T is erased — it disappears at runtime — so inside the function you can't check or create T directly (no x is T, no T()). The type only exists at compile time.
solid answer
~50 sKotlin generics use type erasure on the JVM: a plain fun <T> retains no runtime record of T, so inside the body you cannot do x is T, x as T (gives an unchecked-cast warning), call T::class, or instantiate T() — the compiler rejects them because the type isn't available at runtime. Within this leaf's scope, that's the key constraint on a generic function: T is a compile-time-only contract. The standard escape hatch is making the function inline with a reified type parameter (inline fun <reified T>), which inlines the call and substitutes the concrete type, enabling x is T and T::class. A non-reified alternative is to accept a Class<T> or KClass<T> token explicitly. Without one of these, T stays opaque: you can pass values of type T around and return them, but you can't reflect on or construct the type.
code
kotlin · 10 lines// Default: erased — these would NOT compile
fun <T> bad(x: Any): Boolean {
// return x is T // ERROR: cannot check erased type
// return T::class != null // ERROR: T not reified
return false
}
// Reified makes T real at runtime
inline fun <reified T> Any.asOrNull(): T? = this as? T
val r = (42 as Any).asOrNull<String>() // null, a genuine runtime checkgo deeper
Knows generics are 'erased' and that you mostly just pass T values through.
Lists the forbidden operations (is T, T(), T::class) and that as T is unchecked.
Explains reified+inline and the type-token alternative, and reasons about when each is appropriate and their costs.
Weighs API trade-offs of inline/reified (bytecode size, no virtual dispatch, binary compatibility) versus explicit tokens when designing library functions.
## Type erasure On the JVM, Kotlin (like Java) **erases** generic type parameters: at runtime a `fun <T>` has no information about what `T` actually was for a given call. The compiler uses `T` to type-check the source, then discards it. So `T` is a **compile-time-only** contract. ## What erasure forbids inside the body Because `T` is unknown at runtime, the compiler rejects operations that would need it: ```kotlin fun <T> demo(value: Any) { // if (value is T) { } // ERROR: cannot check for instance of erased type // val t = T() // ERROR: cannot instantiate type parameter // val c = T::class // ERROR: cannot use T as reified type parameter @Suppress("UNCHECKED_CAST") val t = value as T // compiles, but UNCHECKED_CAST — no real runtime check } ``` The `as T` cast compiles but is **unchecked**: it won't actually fail at the cast site even if the type is wrong; a `ClassCastException` may surface later when the value is used as `T`. ## Escape hatch 1: inline + reified Marking the function `inline` and the parameter `reified` makes the compiler **inline the body at each call site and substitute the concrete type**, so `T` becomes real at runtime: ```kotlin inline fun <reified T> Any.asOrNull(): T? = this as? T val s: String? = (42 as Any).asOrNull<String>() // null, real runtime check ``` With `reified` you can use `x is T`, `x as T` (checked), and `T::class`. The cost: the function must be `inline`, so it can't be virtual/stored as a value and may increase bytecode size. ## Escape hatch 2: pass a type token If you can't or don't want to inline, accept the type explicitly: ```kotlin fun <T : Any> parse(json: String, type: KClass<T>): T = TODO() ``` Here the caller passes `T::class`, giving the body a runtime handle. ## What still works without either You can freely **accept, store locally, pass, and return** values of type `T` — erasure doesn't stop ordinary value flow, only **runtime type introspection and construction**. That's why most generic functions (map, filter, identity) need neither reified nor tokens. ## Summary - Default `fun <T>`: `T` erased; no `is T`, no `T()`, no `T::class`; `as T` is unchecked. - `inline fun <reified T>`: `T` available at runtime for checks/reflection. - Or pass `Class<T>`/`KClass<T>` explicitly.
- Why does val t = value as T compile but is risky?It's an unchecked cast: erasure means no real runtime type check happens at the cast, so the cast appears to succeed and a ClassCastException can surface later when the value is actually used as T.
- What two requirements does using x is T inside a function impose?The function must be inline and the type parameter must be marked reified; otherwise the type is erased and the is-check won't compile.
T is like a chalk label rubbed off the box before shipping — useful while packing (compile time), gone when the box arrives (runtime).
saying these in an interview costs you the question
- Claiming you can call T() or T::class on a plain fun <T>
- Thinking as T performs a real runtime check (it's unchecked)
- Believing reified works without inline
- Saying Kotlin generics are reified by default like C#
- Not knowing the KClass/Class token alternative