← Back to list

Reified Keyword in Kotlin

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

Anand Gaur · 2025-11-25 06:09 · 12 claps · 10.3 min read paywalled
#reified #kotlin #android #kotlin-coroutines #programming
Open on Medium ↗
Wiki topics: 💻 · Programming 📱 · Mobile Development

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 T
  • T::class.java
  • T::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

  1. Must be used with inline functions — The inline keyword is mandatory
  2. Only works with type parameters — You can only reify generic type parameters
  3. 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

  1. Only works with inline functions — Can’t use it with regular functions
  2. Can’t be used in class type parameters — Only in function type parameters
  3. Increases code size — Since the function is copied everywhere it’s called
  4. 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:

  1. 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:

  1. 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:

  1. 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.

🔗 **Book a session here**

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