Kotlin's Secret Weapon: How 'Reified' Solves the Mystery of Missing Types
Stop fighting type erasure and start writing cleaner, safer generic code using reified type parameters.
If you’ve spent any time with Kotlin, you’ve likely encountered a frustrating wall: Type Erasure. You write a beautiful generic function, but the moment you try to check if (item is T) or call T::class.java, the compiler shuts you down.
It feels like the language is “forgetting” the types you just gave it. Here is how the reified keyword acts as a bridge, allowing you to use types at runtime that would otherwise be lost.
The Ghost in the Machine: What is Type Erasure?
By default, generics in Kotlin (and Java) are only for the compiler. Once your code is turned into bytecode, the specific type — like String in List<String>—is stripped away to ensure compatibility with the JVM.
At runtime, your List<String> and List<Int> look exactly the same to the Virtual Machine. This is why you can't normally ask a generic T what it is.
The “Old” Way (The Boilerplate Tax)
Before reified, if you wanted a function to know about a type at runtime, you had to manually pass the Class object as a parameter. This is clunky and leads to "API pollution":
// The "Manual" way - verbose and repetitive
fun <T> findItemsOfType(items: List<Any>, clazz: Class<T>): List<T> {
return items.filterIsInstance(clazz)
}
// Call site:
val strings = findItemsOfType(myList, String::class.java)Enter reified: Specialization via Inlining
The reified keyword doesn't actually "defeat" the JVM's type erasure globally. Instead, it uses a clever compiler trick: Specialization at the Call Site.
When you mark a function as inline and its parameter as reified, the compiler replaces every call site with the actual body of the function. Because the specific type is known at the moment of the call, the compiler "bakes" that specific type into the bytecode at that specific location.
The Two Golden Rules:
- Mark the function as
inline. - Add the
reifiedkeyword to the type parameter.
Real-World Power: Practical Examples
1. Cleaner Deserialization (The Serializer Pattern)
Most JSON libraries need to know the target class. Instead of passing User::class.java every time, we can use reified to make our API feel native.
// A safer, more realistic mock of a parsing utility
inline fun <reified T> String.toObject(): T {
val jsonLibrary = MyJsonParser() // Placeholder for Gson/Moshi/Serialization
// We pass T::class.java to the underlying library
return jsonLibrary.fromJson(this, T::class.java)
}
// Usage:
val user = "{\"name\":\"Alex\"}".toObject<User>()2. The “Filter & Cast” Pattern
Imagine you have a list of different UI components and you only want the Button types, already cast and ready to use:
inline fun <reified T : View> List<View>.findFirst(): T? {
for (item in this) {
if (item is T) return item // No 'unchecked cast' warnings!
}
return null
}Comparison: Reified vs. Standard Generics

Critical Limitations & Trade-offs
While reified feels like magic, it has strict professional boundaries:
- No Java Interoperability: Because Java does not support
inlinefunction substitution, Java code cannot call Kotlin functions with reified parameters. If you are building a library for both languages, provide a non-reified overload. - Code Bloat: Since the function body is copied to every call site, keep reified functions small. If the logic is 50 lines long, have the inline function call a private, non-inline function to do the heavy lifting.
- No Constructor Access: You still cannot do
val item = T(). Reification lets you inspect the type, but it doesn't bypass the fact thatTdoesn't have a known constructor at compile time.
Frequently Asked Questions (FAQs)
Can I use reified on a class property?
No. Classes cannot be inlined, so they cannot have reified type parameters. Reification is strictly for functions.
Is there a performance penalty?
Actually, it can be faster. By inlining the code, you remove the overhead of a function call and the overhead of reflection-based type checks. However, the trade-off is a slightly larger binary size.
How do I create a generic array safely?
Since Array<T> requires a concrete component type on the JVM, you must use reflection within your reified function:
inline fun <reified T> createArray(size: Int): Array<T> {
return java.lang.reflect.Array.newInstance(T::class.java, size) as Array<T>
}Think About This…
- How much “boilerplate” code (passing
.classor::class) exists in your current project? - Can you use
reifiedto create a more "DSL-like" experience for your team?
What do you think?
Does reified make Kotlin generics feel "complete" for you, or do you find the inline requirement too restrictive for large-scale architecture? Let's discuss in the comments!
If you find my content useful, feel free to support my work here. Every contribution is greatly appreciated! 🙏

Comments
Post a Comment