Kotlin Reified Types

Normally, generic type information is erased at runtime — the JVM does not know whether a List<Int> is different from a List<String> at runtime. Reified type parameters solve this by preserving the actual type inside inline functions, so you can use it with is, as, and ::class at runtime.

The Type Erasure Problem

// This does NOT work — T is erased at runtime
fun  checkType(value: Any): Boolean {
    return value is T   // ERROR: Cannot check for erased type
}

Reified Type Parameters

// inline + reified = T is available at runtime
inline fun  checkType(value: Any): Boolean {
    return value is T
}

fun main() {
    println(checkType("hello"))    // false
    println(checkType("hello")) // true
    println(checkType(3.14))    // true
    println(checkType(42))         // true
}

How It Works


Normal generic:
  fun  check(value: Any): Boolean = value is T
  At runtime: T is erased → "is T" becomes "is Object" → meaningless

Reified:
  inline fun  check(value: Any): Boolean = value is T
  The compiler COPIES the function body and replaces T with the ACTUAL type:
  check(value)  → inline copy becomes: value is Int
  check(value) → inline copy becomes: value is String

Getting the Class of T

inline fun  printType() {
    println("Type is: ${T::class.simpleName}")
}

inline fun  createTypedList(): List {
    println("Creating list of ${T::class.simpleName}")
    return emptyList()
}

fun main() {
    printType()        // Type is: Int
    printType()     // Type is: String
    printType>()    // Type is: List

    createTypedList()   // Creating list of Double
}

Filtering a List by Type

inline fun  List<*>.filterByType(): List {
    return filterIsInstance()
}

fun main() {
    val mixed: List = listOf(1, "hello", 2.5, "world", 42, true, 3.14)

    val ints    = mixed.filterByType()
    val strings = mixed.filterByType()
    val doubles = mixed.filterByType()

    println("Ints:    $ints")      // [1, 42]
    println("Strings: $strings")   // [hello, world]
    println("Doubles: $doubles")   // [2.5, 3.14]
}

Safe Cast with Reified

inline fun  Any?.safeCast(): T? = this as? T

fun main() {
    val value: Any = "Kotlin"

    val asString = value.safeCast()
    val asInt    = value.safeCast()

    println(asString)   // Kotlin
    println(asInt)      // null (safe cast returns null if wrong type)
}

Reified with Reflection

inline fun  typeNameOf(): String = T::class.qualifiedName ?: "Unknown"

fun main() {
    println(typeNameOf())       // kotlin.String
    println(typeNameOf())          // kotlin.Int
    println(typeNameOf>())      // kotlin.collections.List
}

Practical Example: JSON-Like Deserializer

inline fun  parseValue(raw: String): T? {
    return when (T::class) {
        Int::class    -> raw.toIntOrNull() as? T
        Double::class -> raw.toDoubleOrNull() as? T
        Boolean::class -> raw.toBooleanStrictOrNull() as? T
        String::class -> raw as? T
        else          -> null
    }
}

fun main() {
    val age:  Int?     = parseValue("28")
    val score: Double? = parseValue("95.5")
    val flag: Boolean? = parseValue("true")
    val name: String?  = parseValue("Alice")
    val bad:  Int?     = parseValue("notanumber")

    println("age=$age, score=$score, flag=$flag, name=$name, bad=$bad")
}

Output:

age=28, score=95.5, flag=true, name=Alice, bad=null

Leave a Comment

Your email address will not be published. Required fields are marked *