octantis124
octantis124

Reputation: 45

is-check or a cast that involves the non-generic part of the type

I got a few question when I was reading the Kotlin reference:

In "Type erasure and generic type checks", it mention:

When you already have the type arguments of an instance checked statically (at compile time), you can make an is-check or a cast that involves the non-generic part of the type. Note that angle brackets are omitted in this case

and they gave a example:

fun handleStrings(list: List<String>) {
    if (list is ArrayList) {
        // `list` is smart-cast to `ArrayList<String>`
    }
}

Q1: I test myself

val a = mutableListOf(1, 2, 3)
val b = listOf(1, 2, 3)
println(a is List)   //error, should use List<*>
println(b is MutableList)   //can compile

I don't understand why they got different result.

Q2: I found that I can't use smart cast from List to ArrayList

println(listOf(1, 2, 3) is ArrayList)  //false
println(listOf(1, 2, 3) is MutableList)  //true

As I remember, listOf() is implement by ArrayList in Kotlin, it calls asList, which return a ArrayList, in Arrays.java. So, why can't listOf() smart cast to ArrayList?

Upvotes: 1

Views: 66

Answers (1)

ardenit
ardenit

Reputation: 3890

A1: In the first case, you cast from subclass to superclass. It does not make any sense, since instance of subclass can always be used as an instance of superclass without casting:

val a: MutableList<Int> = mutableList(1, 2, 3)
val b: List<Int> = a

So the Kotlin compiler does not implement generic smart cast for casting upstairs and uses default rules of casting which require star projection.

In the second case you cast from superclass to subclass, so it works as intended.

A2: There are two different ArrayList classes. Try to run this code:

println(listOf(1, 2, 3)::class)
println(ArrayList<String>()::class)

It prints:

class java.util.Arrays$ArrayList
class java.util.ArrayList

listOf returns a nested class Arrays.ArrayList, not ArrayList you usually work with.

Upvotes: 3

Related Questions