← Back to Kotlin Course | Chapter 11: Generics | Lesson 5 of 6

Type Erasure

Type erasure means that once your code is compiled, the JVM forgets exactly which generic type a container was holding, only remembering the container itself.
Syntax
kotlin
// Erased at runtime: cannot check the type argument
// if (list is List<String>)  // error
if (list is List<*>) { /* only the raw type is checked */ }

// reified keeps the type argument inside an inline function
inline fun <reified T> functionName(value: Any) = value is T

Generic Type Information Is Erased

Once compiled, the JVM no longer distinguishes between a List<String> and a List<Int> -- both are simply represented as a plain List at runtime, which is what 'type erasure' means.

Example: Generic Type Information Is Erased

markup
// Entry point: execution of the program starts here
fun main() {
    // Declare a read-only variable `strings` (a `List<Any>`), set to `listOf("a", "b")`
    val strings: List<Any> = listOf("a", "b")
    // Declare a read-only variable `numbers` (a `List<Any>`), set to `listOf(1, 2)`
    val numbers: List<Any> = listOf(1, 2)
    // Print "Same runtime class: ${strings::class == numbers::class}" to the console, with a trailing newline
    println("Same runtime class: ${strings::class == numbers::class}")
}

Why You Cannot Check is List<String>

Because the element type is erased, Kotlin does not allow checking is List<String> directly -- only whether something is a List at all, using a star projection like is List<*>.

Example: Why You Cannot Check is List<String>

markup
// Define the function `describe` taking `value`
fun describe(value: Any) {
    // Check whether `value is List<*>`
    if (value is List<*>) {
        // Print "It is some kind of List with ${value.size} elements" to the console, with a trailing newline
        println("It is some kind of List with ${value.size} elements")
    }
}

// Entry point: execution of the program starts here
fun main() {
    describe(listOf(1, 2, 3))
    describe(listOf("a", "b"))
}

Working Around Erasure with reified

Because plain generic functions cannot see the real type at runtime, the reified keyword on an inline function's type parameter is the standard workaround when a runtime type check is genuinely needed.

Example: Working Around Erasure with reified

markup
inline fun <reified T> countOfType(items: List<Any>): Int = items.count { it is T }

// Entry point: execution of the program starts here
fun main() {
    // Declare a read-only variable `mixed`, set to `listOf(1, "a", 2, "b", 3)`
    val mixed = listOf(1, "a", 2, "b", 3)
    // Print "Number of Strings: ${countOfType<String>(mixed)}" to the console, with a trailing newline
    println("Number of Strings: ${countOfType<String>(mixed)}")
}

Arrays Are Not Erased

Unlike generic collections, arrays in Kotlin (backed by JVM arrays) retain their component type information at runtime, so is Array<String>-style checks behave differently from generic collections.

Example: Arrays Are Not Erased

markup
// Entry point: execution of the program starts here
fun main() {
    // Declare a read-only variable `stringArray` (a `Array<String>`), set to `arrayOf("a", "b")`
    val stringArray: Array<String> = arrayOf("a", "b")
    // Print "Array component type: ${stringArray.javaClass.componentType}" to the console, with a trailing newline
    println("Array component type: ${stringArray.javaClass.componentType}")
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Trying to check if (list is List<String>), which the compiler rejects since the element type is erased and cannot be checked at runtime.
  2. Assuming two List<Int> and List<String> are different runtime classes; at runtime they are both simply the same List class.
  3. Forgetting that arrays are an exception -- unlike generic collections, arrays retain their element type information at runtime (reified natively by the JVM).
Chapter Summary
  • At runtime, generic type parameters are erased -- a List<String> and a List<Int> are both just List once compiled.
  • You cannot check is List<String> directly; only an unchecked cast or a star-projected check like is List<*> is allowed.
  • Type erasure is a JVM-wide limitation, not specific to Kotlin, and it's why reified type parameters (with inline) exist as a workaround.
  • Arrays are an exception to erasure -- their component type is preserved by the JVM at runtime.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.