I was wondering why, since if (x is MyGeneric<Int>) { ... }
will crash due to type erasure, as? works. I mean, couldn't A is B
be implemented as (A as? B).let { if (it == null) false else true }
? If yes, why can't is
checks work against generics, if no, what does differ ?
When you cast a class that has generic types, the cast will always succeed if the generic type is the only thing that is wrong. And type erasure, which you seem to be familiar with, is the reason. At runtime, a List<String>
and a List<Foo>
are exactly the same thing because of type erasure so casting between them will always succeed.
So a safe cast does not protect you from getting the generic type wrong, only the class type.
as?
works
No it does not. as?
does not return null, even when the generic type arguments are not compatible:
val strings = listOf("x", "y")
val ints = strings as? List<Int>
The above code will compile and run without any errors. It will only throw an exception, when you actually take things out of ints
, and try to use them as Int
s. The reason why this happens is, as you said, type erasure.
is
could have been designed to work the same way as as?
- evaluating to true
even when the generic type arguments are not compatible:
val strings = listOf("x", "y")
strings is List<Int> // this could be designed to evaluate to true
The designers of Kotlin decided to disallow x is Foo<T>
, but allow x as? Foo<T>
.
After all, "checking the type" and "type conversion" come with different expectations. If you are asking me if an instance of List<Int>
is of type List<Int>
, you'd be very unexpected if I said yes. On the other hand, if you are asking me to convert an instance of List<Int>
to a List<String>
, and I successfully did it (albeit without checking the list's contents), that's not as unexpected. After all, I did what you asked for.
Now to answer your question:
I mean, couldn't A is B be implemented as
(A as? B).let { if (it == null) false else true }
? If yes, why can'tis
checks work against generics?
It could, but this would produced some rather unexpected behaviours, as discussed in the previous paragraph. Therefore, it is designed to not allow it.