Reified Keyword in Kotlin
Kotlin gives us many powerful features, but one keyword that often confuses developers — especially beginners — is reified. At first, it…
Reified Keyword in Kotlin

Kotlin gives us many powerful features, but one keyword that often confuses developers — especially beginners — is reified. At first, it looks complicated, but once you understand what it does, it becomes one of the most useful tools in your Kotlin toolbox.
In simple words, Kotlin normally hides the actual type used inside a generic function. This creates limitations when you want to check types or use reflection. The reified keyword steps in and solves this exact problem by giving you access to the real type at runtime.
Whether you’re working with JSON parsing, writing helper functions in Android, or building reusable utilities, reified helps you write cleaner and smarter code.
In this blog, we’ll break down this keyword in the easiest possible way—with simple explanations and real-world examples that make everything crystal clear.
What is the Reified Keyword?
The reified keyword in Kotlin is a special feature that allows you to access type information at runtime within inline functions. Normally, due to Java's type erasure, generic type information is lost at runtime. But with reified, Kotlin gives you a way to work around this limitation.
**reified = keep the type (T) alive at runtime.**
So you can use things like:
value is TT::class.javaT::class.simpleName
The Problem: Type Erasure
Before understanding reified, let's understand the problem it solves.
In Java and Kotlin, when you use generics, the type information is erased at runtime. This means:
fun <T> printType() {
// This won't work! Type T is erased at runtime
// println(T::class.java)
}
This happens because Java Virtual Machine (JVM) doesn’t store generic type information at runtime for backward compatibility reasons.
The Solution: Reified Types
Kotlin’s reified keyword, when used with inline functions, preserves type information at runtime:
inline fun <reified T> printType() {
println(T::class.java.simpleName)
}
// Usage
printType<String>() // Output: String
printType<Int>() // Output: Int
Key Rules for Using Reified
- Must be used with inline functions — The
inlinekeyword is mandatory - Only works with type parameters — You can only reify generic type parameters
- The function is inlined at call site — The compiler copies the function body to where it’s called
Real-World Examples
Example 1: JSON Parsing
One of the most common use cases is parsing JSON into different types:
import com.google.gson.Gson
inline fun <reified T> Gson.fromJson(json: String): T {
return this.fromJson(json, T::class.java)
}
// Usage
val gson = Gson()
val user = gson.fromJson<User>("""{"name":"anand","age":30}""")
val product = gson.fromJson<Product>("""{"title":"Phone","price":599}""")
Without reified, you'd have to pass the class explicitly:
// Without reified - more verbose
val user = gson.fromJson(jsonString, User::class.java)
Example 2: Type Checking
Checking if an object is of a certain type becomes much cleaner:
inline fun <reified T> Any.isInstanceOf(): Boolean {
return this is T
}
// Usage
val obj: Any = "Hello"
println(obj.isInstanceOf<String>()) // true
println(obj.isInstanceOf<Int>()) // false
Example 3: Finding Elements by Type
A practical example for filtering collections:
inline fun <reified T> List<Any>.filterByType(): List<T> {
return this.filterIsInstance<T>()
}
// Usage
val mixedList = listOf(1, "Hello", 2, "World", 3.14, true)
val strings = mixedList.filterByType<String>()
val numbers = mixedList.filterByType<Int>()
println(strings) // [Hello, World]
println(numbers) // [1, 2]
Example 4: Intent Extras in Android
A very practical Android example for retrieving Intent extras:
inline fun <reified T : Activity> Context.startActivity() {
val intent = Intent(this, T::class.java)
startActivity(intent)
}
// Usage - Much cleaner!
startActivity<MainActivity>()
startActivity<ProfileActivity>()
// Without reified, you'd write:
startActivity(Intent(this, MainActivity::class.java))
Example 5: Shared Preferences Helper
inline fun <reified T> SharedPreferences.get(key: String, defaultValue: T): T {
return when (T::class) {
String::class -> getString(key, defaultValue as String) as T
Int::class -> getInt(key, defaultValue as Int) as T
Boolean::class -> getBoolean(key, defaultValue as Boolean) as T
Float::class -> getFloat(key, defaultValue as Float) as T
Long::class -> getLong(key, defaultValue as Long) as T
else -> throw IllegalArgumentException("Unsupported type")
}
}
// Usage
val userName = prefs.get<String>("user_name", "Guest")
val loginCount = prefs.get<Int>("login_count", 0)
val isFirstTime = prefs.get<Boolean>("is_first_time", true)
How Does It Work?
When you use reified with inline functions, the compiler performs what's called "inlining". Let's see what happens:
inline fun <reified T> createInstance(): T {
return T::class.java.newInstance() as T
}
// When you call:
val myString = createInstance<String>()
// The compiler transforms it to:
val myString = String::class.java.newInstance() as String
The function body is literally copied to the call site, and the type parameter is replaced with the actual type!
Limitations of Reified
- Only works with inline functions — Can’t use it with regular functions
- Can’t be used in class type parameters — Only in function type parameters
- Increases code size — Since the function is copied everywhere it’s called
- Can’t be used with Java — Reified is a Kotlin-only feature
When Should You Use Reified?
Use reified when you:
- Need to access type information at runtime
- Want cleaner API for users of your functions
- Are working with reflection
- Need to perform type checks or casts
- Want to avoid passing Class<T> parameters manually
When not to use it?
- If the function is very big (since inline copies code everywhere)
- If you don’t need type information at runtime
Comparison: With vs Without Reified
Without Reified:
fun <T> loadData(clazz: Class<T>, id: String): T {
// Must pass Class object explicitly
return database.query(clazz, id)
}
val user = loadData(User::class.java, "123") // Verbose
With Reified:
inline fun <reified T> loadData(id: String): T {
// Type is available directly
return database.query(T::class.java, id)
}
val user = loadData<User>("123") // Clean and concise
Some interview questions related to the reified keyword
Question 1: What is the use case of the reified keyword in Kotlin?
The reified keyword lets you access the actual type (like String, Int, User) inside a generic function at runtime.
Use Cases:
1. JSON Parsing
// Without reified - ugly
fun <T> parseJson(json: String, clazz: Class<T>): T {
return Gson().fromJson(json, clazz)
}
val user = parseJson(jsonString, User::class.java) // Have to pass class
// With reified - beautiful
inline fun <reified T> parseJson(json: String): T {
return Gson().fromJson(json, T::class.java)
}
val user = parseJson<User>(jsonString) // Clean!
2. Type Checking
inline fun <reified T> isType(value: Any): Boolean {
return value is T
}
println(isType<String>("Hello")) // true
println(isType<Int>("Hello")) // false
3. Finding Elements in Collections
inline fun <reified T> List<*>.findByType(): List<T> {
return filterIsInstance<T>()
}
val mixed = listOf(1, "hello", 2, "world")
val strings = mixed.findByType<String>() // [hello, world]
4. Android Navigation
inline fun <reified T : Activity> Context.launch() {
startActivity(Intent(this, T::class.java))
}
launch<MainActivity>() // So clean!
Question 2: Why does the casting issue occur in generic functions?
Java and Kotlin erase (delete) type information at runtime for backward compatibility. So when your code runs, T becomes just Object - the type is gone!
The Problem in Detail:
fun <T> processData(data: Any): T {
// ❌ ERROR: Cannot check for instance of erased type: T
if (data is T) {
return data
}
// ❌ ERROR: Cannot access class of type parameter T
// val clazz = T::class.java
return data as T // ⚠️ This is UNCHECKED cast - dangerous!
}
Why This Happens:
- At Compile Time:
fun <T> getValue(): T { ... }
val result: String = getValue<String>()
2. At Runtime (after type erasure):
fun getValue(): Object { ... } // T is gone!
val result: String = (String) getValue() // Cast happens here
Real Example:
fun <T> createList(): List<T> {
return listOf() as List<T> // Unchecked cast warning!
}
val stringList: List<String> = createList()
val intList: List<Int> = createList()
// At runtime, both are just List<Object>!
// The JVM doesn't know one should be String and other should be Int
Why Erasure Exists:
- Java generics were added in Java 5 (2004)
- Had to work with older Java code (Java 1, 2, 3, 4)
- Solution: Delete type info at runtime, keep bytecode compatible
Question 3: What specific task does the reified keyword perform to solve the casting issue?
reified makes the compiler copy the actual type into the function body when it's inlined, so the type information is preserved.
Step-by-Step Explanation:
Without Reified:
fun <T> getClassName(): String {
// ❌ Can't do this - T is erased!
// return T::class.java.simpleName
return "Unknown"
}
With Reified:
inline fun <reified T> getClassName(): String {
// ✅ Works! Type is available
return T::class.java.simpleName
}
What Happens Behind the Scenes:
Your Code:
inline fun <reified T> printType() {
println("Type is: ${T::class.simpleName}")
}
fun main() {
printType<String>()
printType<Int>()
printType<User>()
}
What Compiler Does (Inlining):
fun main() {
// printType<String>() becomes:
println("Type is: ${String::class.simpleName}")
// printType<Int>() becomes:
println("Type is: ${Int::class.simpleName}")
// printType<User>() becomes:
println("Type is: ${User::class.simpleName}")
}
Key Tasks Reified Performs:
- Preserves Type Information
inline fun <reified T> isInstance(obj: Any): Boolean {
return obj is T // ✅ Works! Type check is real
}
2. Enables Type Casting
inline fun <reified T> castTo(obj: Any): T? {
return obj as? T // ✅ Safe cast with real type
}
3. Allows Reflection
inline fun <reified T> createInstance(): T {
return T::class.java.newInstance() // ✅ Can create instance
}
4. Enables Class Reference Access
inline fun <reified T> getClass(): Class<T> {
return T::class.java // ✅ Can access Class object
}
Question 4: How can developers verify that type erasure is happening?
You can write code that compiles but crashes at runtime, or use bytecode inspection tools.
Method 1: Runtime Crash Test
fun <T> unsafeTypeCheck(value: Any): Boolean {
// This compiles but gives warning
return value is T // ❌ Will not work as expected
}
fun main() {
// This will compile but has issues
println(unsafeTypeCheck<String>("Hello")) // Seems to work
println(unsafeTypeCheck<String>(123)) // Also seems to work!
// Because T is erased, it can't actually check the type
}
Method 2: Reflection Test
fun testTypeErasure() {
val stringList: List<String> = listOf("A", "B")
val intList: List<Int> = listOf(1, 2)
// Check their classes at runtime
println(stringList::class.java) // class java.util.Arrays$ArrayList
println(intList::class.java) // class java.util.Arrays$ArrayList
// Both are the SAME class at runtime!
println(stringList::class.java == intList::class.java) // true
// Type parameter is erased!
}
Method 3: Using Java Reflection
fun <T> inspectType(list: List<T>) {
val type = list::class.java.genericSuperclass
println("Runtime type: $type")
// You'll see it's just "List", not "List<String>" or "List<Int>"
}
fun main() {
inspectType(listOf("a", "b")) // Just shows List
inspectType(listOf(1, 2)) // Just shows List
}
Method 4: Bytecode Verification
Write this Kotlin code:
fun <T> process(item: T) {
println(item)
}
fun main() {
process<String>("Hello")
process<Int>(42)
}
Check bytecode using:
kotlinc MyFile.kt
javap -c MyFileKt.class
You’ll see in bytecode:
public static final void process(Ljava/lang/Object;)V
// Note: parameter is Object, not T!
Method 5: Practical Test — ClassCastException
fun demonstrateErasure() {
// This compiles fine
val list: Any = listOf(1, 2, 3)
// Unchecked cast - compiles with warning
val stringList = list as List<String>
// Crashes at runtime when you try to use it!
try {
val first: String = stringList[0] // 💥 ClassCastException!
} catch (e: ClassCastException) {
println("Type erasure caught! Can't enforce List<String> at runtime")
}
}
Question 5: What other keyword must be added when using the reified keyword in Kotlin?
You MUST use the inline keyword. Without it, reified won't work!
Why Inline is Required:
// ❌ ERROR: This will NOT compile
fun <reified T> process() {
// Error: reified type parameters can only be used in inline functions
}
// ✅ CORRECT: Must be inline
inline fun <reified T> process() {
// Works!
}
Reason Behind This:
- Inlining copies the function body
inline fun <reified T> getName() = T::class.simpleName
fun main() {
getName<String>() // Function body is copied here
getName<Int>() // And here
}
2. During copying, actual type is substituted
// What you write:
inline fun <reified T> show() {
println(T::class.simpleName)
}
show<User>()
// What compiler generates:
println(User::class.simpleName) // T is replaced with User
Complete Syntax:
// Basic reified function
inline fun <reified T> example1() { }
// With return type
inline fun <reified T> example2(): T { }
// With parameters
inline fun <reified T> example3(value: String): T { }
// Multiple type parameters
inline fun <reified T, reified R> example4(): Pair<T, R> { }
// With constraints
inline fun <reified T : Number> example5(): T { }
What Happens Without Inline:
// Without inline, function is called normally:
fun <T> regularFunction() {
// T information is erased at runtime
}
// With inline, function body is copied:
inline fun <reified T> inlineFunction() {
// T information is preserved because code is copied
}
Question 6: When converting JSON to a data class object, what challenge occurs if a generic extension function is used without the reified keyword?
You can’t tell the JSON parser what type to create because the type information is erased. You’d have to manually pass the class.
The Problem Without Reified:
// ❌ This DOESN'T work
fun <T> String.fromJson(): T {
// ERROR: How do we tell Gson what type T is?
// return Gson().fromJson(this, ???)
// We have no way to get T::class.java here!
}
Why It Fails:
val jsonString = """{"name":"John","age":30}"""
// At compile time:
val user: User = jsonString.fromJson<User>()
// At runtime (after type erasure):
val user: User = jsonString.fromJson() // T is erased!
// Gson has NO IDEA it should create a User object
The Workaround Without Reified (Ugly):
// Have to pass the class manually
fun <T> String.fromJson(clazz: Class<T>): T {
return Gson().fromJson(this, clazz)
}
// Usage - not clean
val user = jsonString.fromJson(User::class.java) // Ugly!
val product = jsonString.fromJson(Product::class.java) // Repetitive!
The Solution With Reified (Beautiful):
// ✅ Clean solution
inline fun <reified T> String.fromJson(): T {
return Gson().fromJson(this, T::class.java) // T is available!
}
// Usage - beautiful!
val user = jsonString.fromJson<User>()
val product = jsonString.fromJson<Product>()
Real-World Example:
data class User(val name: String, val age: Int)
data class Product(val title: String, val price: Double)
fun testJsonParsing() {
val userJson = """{"name":"Alice","age":25}"""
val productJson = """{"title":"Phone","price":599.99}"""
// WITHOUT reified (the old way):
fun <T> parseOldWay(json: String, clazz: Class<T>): T {
return Gson().fromJson(json, clazz)
}
val user1 = parseOldWay(userJson, User::class.java)
val product1 = parseOldWay(productJson, Product::class.java)
// WITH reified (the new way):
inline fun <reified T> String.parse(): T {
return Gson().fromJson(this, T::class.java)
}
val user2 = userJson.parse<User>()
val product2 = productJson.parse<Product>()
// Much cleaner, right?
}
Detailed Challenge Breakdown:
1. Parser Needs Class Information:
// Gson needs to know what object to create
gson.fromJson(json, User::class.java) // ← Needs this class info
2. Generic Type is Erased:
fun <T> String.toObject(): T {
// At runtime, T is gone!
// How do we tell Gson to create T?
}
3. No Way to Get Class Without Reified:
fun <T> example() {
// ❌ Can't do this:
// val clazz = T::class.java
// ❌ Can't do this:
// val instance = T()
// ❌ Can't do this:
// if (something is T)
}
4. Reified Solves All Three:
inline fun <reified T> example() {
// ✅ Can do all of these:
val clazz = T::class.java
val instance = T::class.java.newInstance()
if (something is T) { }
}
Summary
The reified keyword is a powerful Kotlin feature that makes working with generics much more pleasant. It eliminates the need for passing class references manually and makes your code cleaner and more type-safe. While it has some limitations, when used appropriately, it significantly improves code readability and developer experience.
The key takeaway: reified = inline + accessing type information at runtime!
Thank you for reading. 🙌🙏✌.
Need 1:1 Career Guidance or Mentorship?
If you’re looking for personalized guidance, interview preparation help, or just want to talk about your career path in mobile development — you can book a 1:1 session with me on Topmate.
I’ve helped many developers grow in their careers, switch jobs, and gain clarity with focused mentorship. Looking forward to helping you too!
Found this helpful? Don’t forgot to clap 👏 and follow me for more such useful articles about Android development and Kotlin or buy us a coffee here ☕
Crack Android Interviews Like a Pro
Your complete Android interview preparation book — packed with real questions, deep explanations, and practical insights to help you stand out. 👉 Grab your copy now: https://medium.com/@anandgaur2207/crack-android-interviews-with-confidence-the-only-handbook-youll-need-b87ec525f19c
𝗕𝗼𝗼𝗸 𝗣𝗿𝗲𝘃𝗶𝗲𝘄: https://drive.google.com/file/d/1uq8HUzp6tx63lrkw_vRoTxILdAFJuUwc/view?usp=sharing
If you need any help related to Mobile app development. I’m always happy to help you.
Follow me on:
**LinkedIn, [Github](https://github.com/anandgaur22), [Instagram](https://www.instagram.com/tech.anandgaur) , YouTube & [WhatsApp](https://wa.me/9807407363)**
메타데이터
- post_id
- f1519c4da7b3
- slug
- reified-keyword-in-kotlin-f1519c4da7b3
- url
- https://medium.com/@anandgaur2207/reified-keyword-in-kotlin-f1519c4da7b3
- canonical_url
- https://medium.com/@anandgaur2207/reified-keyword-in-kotlin-f1519c4da7b3
- author_url
- https://medium.com/@anandgaur2207
- status
- ok
- fetched_at
- 2026-07-18 11:33:39