6

In my Kotlin Android project, I made a FileItem class which extends Serializable

class FileItem(<parameters>) : Serializable, Comparable<FileItem> {

So I needed to Serialize instances of this class into a Bundle

val arguments:Bundle = Bundle()
arguments.putSerializable("folders", folders as Serializable)

where folders has been declared as :

folders:Array<FileItem> (method parameter)

The serialization code above compile without any warning. Meanwhile, the problem comes when I need to unserialize folders items :

val arguments: Bundle? = getArguments()
if (arguments != null){
    foldersItems = arguments.getSerializable("folders") as Array<FileItem>

where foldersItems is declared as

var foldersItems: Array<FileItem>?

I get the following warning, that I can't manage to solve without suppress_warning annotation :

w: <Path to my class>: (78, 28): Unchecked cast: java.io.Serializable! to kotlin.Array<com.loloof64.android.chess_positions_archiver.main_file_explorer.FileItem>

This kind of code compiles in Java/Groovy without warning (folderItems is then a FileItem[]), so how can I modify the kotlin code for the compiler to be "satisfied" ?

I noticed in official Kotlin documentation that Kotlin Array does not extend Serializable and is not open for inheritance. Is it possible meanwhite to "add" it via a kind of extension method ?

hotkey
  • 140,743
  • 39
  • 371
  • 326
loloof64
  • 5,252
  • 12
  • 41
  • 78
  • 1
    As to the arrays serialization, they are serializable in Kotlin, too. This snippet shows it: https://gist.github.com/h0tk3y/bc892327ab239121e76df645d32d9832. I don't know why the reference doesn't state it, maybe it's because `Array` is compiled into Java's `SomeType[]`, which is surely serializable. – hotkey Apr 18 '16 at 08:18

2 Answers2

5

In fact, the cast is not unchecked, the compiler's warning is misleading.

This happens because in Kotlin arrays are represented by generic class Array<T>, and the compiler treats it as usual generic class with type parameters erased at runtime.

But on JVM arrays have reified types, and when you cast something as Array<SomeType>, the generated bytecode really checks the type parameter to be SomeType as well as something being an Array<*>, which would only happen for any other generic class.

This example shows that the array cast is checked:

val a: Any = Array<Int>(1) { 0 }

val i = a as Array<Int>
val d = a as Array<Double> // gets checked and throws ClassCastException

The easiest solution is indeed to @Suppress("UNCHECKED_CAST"), because actually there should not be any warning.

I filed an issue describing the problem in Kotlin issue tracker.

Community
  • 1
  • 1
hotkey
  • 140,743
  • 39
  • 371
  • 326
  • Ok. Sorry for the duplicate, your link wasn't purposed when I was creating my discussion. – loloof64 Apr 18 '16 at 08:12
  • Maybe an easy question, but where would you put exactly the annotation in my example (where I try to retrieve the Serializable into the folders array) : I did not manage to suppress the warning. I tried foldersItems = @Suppress("UNCHECKED_CAST") arguments.getSerializable("folders") as Array – loloof64 Apr 18 '16 at 08:22
  • 1
    @loloof64 This should work: `foldersItems = @Suppress("UNCHECKED_CAST") (arguments.getSerializable("folders") as Array)`. Or just annotate the enclosing function. – hotkey Apr 18 '16 at 08:28
1

The cast here is unchecked because the compiler here can't ensure the nullability of array's generic type parameter.

Consider the following example:

fun castAsArrayOfString(param: Any) = param as Array<String>

castAsArrayOfString(arrayOf("a")) // is Array<String>, all ok

castAsArrayOfString(arrayOf("a", null)) // is Array<String>, but contains null

So the compiler warns you about potential type safety problems this cast could introduce.

Ilya
  • 21,871
  • 8
  • 73
  • 92