Search code examples
kotlinimmutable-collections

(imutable collection is mutable) returns true in Kotlin


Why this is happening in Kotlin:

val list: List<Int> = listOf(1, 2, 3)// Immutable list

if(list is MutableCollection<*>){// why this "if" condition is true?
    println("is mutable")// this line is printed
    (list as MutableCollection<Int>).add(4) // this results to java.lang.UnsupportedOperationException
}

list is MutableCollection returns true that shows Kotlin Immutable collection objects implements MutableCollection interface but instead of changing items in collection it throws UnsupportedOperationException

Is it true? and if yes why Immutable collection objects implement MutableCollection interface in Kotlin?

Is it because of Kotlin collections inherit from Java Collections and altering methods (add, remove, ...) already is there and the only way to avoid altering collection is to override it and throw an exception (even if this is true it is not necessary for Kotlin immutable collection objects to implement MutableCollection interface because java altering collection methods already are there and can be overridden)?


Solution

  • No, that's not the correct explanation. This code should help you understand what's going on:

    val list: List<Int> = listOf(1, 2, 3) 
    
    println("list class is = ${list::class.java}")
    
    if(list is MutableCollection<*>) {
        println("is mutable") 
        (list as MutableList<Int>)[0] = 42
        println(list)
    }
    

    The output is

    list class is = class java.util.Arrays$ArrayList
    is mutable
    [42, 2, 3]
    

    So, the explanation is that listOf(1, 2, 3) returns an Arrays$ArrayList list, i.e. the list that would be returned in Java by doing Arrays.asList(1, 2, 3). It is a mutable list, but you can't add anything to it, because it has a fixed size, since it's backed by an array.

    Kotlin lists aren't truly immutable. They just don't have any method allowing to mutate them: they're just immutable interfaces that only expose the readonly methods of an actually mutable list. If you cheat and cast the list to a mutable list, then, if the list is in fact a Java List, the cast will succeed, but you can't know if you're actually be able to mutate them, just like in Java: a List could be an emptyList, which can't be mutated at all, or a non-resizable list as in the above example, or a fully mutable list like an ArrayList.