Not quite.
Without type erasure, List<String>
would remain List<String>
at runtime, so you could write code like this:
if( a is List<String> ) {
// do something for lists of strings, but not for other lists
}
However, with type erasure, the type parameters are literally erased. List<String>
and List<Int>
are the same type at runtime.
That means if you have an Any
parameter, you can check if it’s a list, but not what kind:
fun myFunction( x: Any ) {
if( x is List ) {
// it's a list... but what kind?
}
}
If you want to know what kind of list it is, you have to either store that information separately, or you have to check every element of the list (and what do you do if it’s empty?)
fun myFunction( x: Any ) {
if( x is List ) {
if( x.isEmpty() ) throw IllegalStateException() // We don't know what kind of list
if( x.all { it is String} ) {
// We have a list of strings
}
}
}
It’s called type erasure because the generic type parameter really is erased by the compiler, so the information is not available at runtime.
It means you can do stupid things like:
val myStrings : MutableList<String> = mutableListOf<String>( "This is a string!" )
val v : Any = myStrings
(v as! MutableList<Int>).add(123)
// At this point myStrings is a MutableList<String> but it contains an Int!
If we didn’t have type erasure, it would be possible for the add()
method of list to check the type of the parameter and raise an exception if it didn’t match the list’s generic type. We get that safety from the compiler, but not at runtime. This is why you get warnings about unchecked casts: It happen when the compiler can’t be sure the cast will be safe.