Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast Any to Array in Kotlin

Tags:

kotlin

I'm initializing a class by loading data from a Map<String, Any> in Kotlin. As this Map is gleaned directly from JSON, I don't know for certain that any given key exists, or that its value is of the type I expect. To unpack this Map safely I'm doing the following, which appears to work perfectly:

a = rawData["A"] as? String ?: ""

Some of this data is in further nested JSON, which I'm unpacking to Arrays; I've tried to do this in the same way:

b = rawData["B"] as? Array<String> ?: arrayOf<String>()

However, when I attempt this using an array (as above) IntelliJ kicks up a fuss, saying

Warning:(111, 30) Kotlin: Unchecked cast: Any? to Array<String>

Is this just the IDE getting itself in a twist or is this method genuinely unsafe for Arrays despite being seemingly perfectly safe for other types?

like image 417
Cailean Wilkinson Avatar asked Jul 13 '17 16:07

Cailean Wilkinson


People also ask

How do you cast objects in Kotlin?

In Smart Casting, we generally use is or !is an operator to check the type of variable, and the compiler automatically casts the variable to the target type, but in explicit type casting we use as operator. Explicit type casting can be done using : Unsafe cast operator: as.

How do you create an array of objects in Kotlin?

There are two ways to define an array in Kotlin. We can use the library function arrayOf() to create an array by passing the values of the elements to the function. Since Array is a class in Kotlin, we can also use the Array constructor to create an array.

How do I add an object to an ArrayList in Kotlin?

In this example, we will see how we can define an ArrayList in Kotlin and add an item in the list. We can do it using the library function add() or we can use the "+=" operator. In order demonstrate, we will be creating two ArrayLists, one is of mutable type and the other is of immutable type.


2 Answers

For any future readers of this question, to expand on the accepted answer with a solution:

To safely cast Any to an array of a particular type in Kotlin, you have to first cast to an untyped array (see zsmb13's answer above for why), and then filter that array to the desired type.

For example, to cast input: Any to an array of String instances, you would call:

val inputAsArray = (input as? Array<*>)?.filterIsInstance<String>()
like image 85
Cailean Wilkinson Avatar answered Sep 23 '22 22:09

Cailean Wilkinson


I was ready to call this a bug, because Array is a reified type, meaning its generic parameter can actually be checked at runtime (unlike a List, for example). I've tried looking to see if it's been filed yet, and it turns out the compiler is actually right to show you a warning. As you can see in the response to this issue, there's still a nullability problem with casts of this kind.

val a = arrayOf("foo", "bar", null) as Array<String>
println(a[2].length)

Arrays like the one in this example are successfully cast (using as, they don't throw an exception, using as?, they don't return null), however, the cast can not ensure that this is an Array<String>, only that it's an Array<String?>.

This means that you can later read null values from a variable that is typed as an Array<String> after the cast, with no further warnings from the compiler.

like image 32
zsmb13 Avatar answered Sep 20 '22 22:09

zsmb13