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