Type Erasure
In this page:
// 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
// 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}")
}
Login to try C/C++/Java/PHP code in the editor
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>
// 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"))
}
Login to try C/C++/Java/PHP code in the editor
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
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)}")
}
Login to try C/C++/Java/PHP code in the editor
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
// 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}")
}
Login to try C/C++/Java/PHP code in the editor
- Trying to check
if (list is List<String>), which the compiler rejects since the element type is erased and cannot be checked at runtime. - Assuming two
List<Int>andList<String>are different runtime classes; at runtime they are both simply the sameListclass. - Forgetting that arrays are an exception -- unlike generic collections, arrays retain their element type information at runtime (reified natively by the JVM).
- At runtime, generic type parameters are erased -- a
List<String>and aList<Int>are both justListonce compiled. - You cannot check
is List<String>directly; only an unchecked cast or a star-projected check likeis List<*>is allowed. - Type erasure is a JVM-wide limitation, not specific to Kotlin, and it's why
reifiedtype parameters (withinline) 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: