Issue
This Content is from Stack Overflow. Question asked by Ken Katagiri
I’ve created 2 kotlin methods: one to check a type and another to cast an object. They look like:
fun Any?.isOfType(type: Class<*>): Boolean{
return type.isInstance(this)
// return `this is T` does NOT work.
}
and
fun <T> Any?.castToType(): T {
return this as T
// Works, albeit with a warning.
}
I’ve read some posts on generics and erasures, but I can’t get over what seems to be a discrepancy.
Why is it that checking for the type of an object cannot be done with generics, but casting to a generic can?
Currently working with Kotlin but probably relevant to Java too, so including the Java tag.
Solution
The question is why:
fun <T> Any?.castToType() = this as T // compiles with warning
"hello".castToType<Int>()
"works" but this won’t even compile:
fun <T> Any?.isOfType() = this is T // won't compile
"hello".isOfType<Int>()
Actually both don’t really work. In both cases the type is erased at runtime. So why does one compile and the other doesn’t?
this is T
cannot work at runtime since the type of T is unknown and thus the compiler has to reject it.
this as T
on the other hand might work:
"hello".castToType<Int>() // no runtime error but NOP
"hello".castToType<Int>().minus(1) // throws ClassCastException
2.0.castToType<Int>().minus(1) // no runtime error, returns 1
In some cases it works, in others it throws an exception. Now every unchecked cast can either succeed or lead to runtime exceptions (with or without generic types) so it makes sense to show a warning instead of a compile error.
Summary
- unchecked casts with generic types are no different from unchecked casts without generic types, they are dangerous but a warning is sufficient
- type checks with generic types on the other hand are impossible at runtime
Addendum
The official documentation explains type erasure and why is-checks with type arguments can’t succeed at runtime:
At runtime, the instances of generic types do not hold any information about their actual type arguments. The type information is said to be erased. For example, the instances of Foo and Foo<Baz?> are erased to just Foo<*>.
Due to the type erasure, there is no general way to check whether an instance of a generic type was created with certain type arguments at runtime, and the compiler prohibits such is-checks such as ints is List or list is T (type parameter)
(https://kotlinlang.org/docs/generics.html#type-erasure)
In my own words: I can’t check whether A is B if I don’t know what B is. If B is a class I can check against an instance of that class (that’s why type.isInstance(this)
works) but if B is a generic type, the runtime has no information on it (it was erased by the compiler).
This Question was asked in StackOverflow by Ken Katagiri and Answered by Emanuel Moecklin It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.