Search code examples
kotlinmutabilitymutablelist

Why mutable lists of different types behave as immutable when put inside a mutable list


As we know, it is possible in Kotlin to create a mutable list of mutable lists of different types, e.g.:

val list = mutableListOf(
            MutableList<Int>(3) { 0 }, 
            MutableList<String>(2) { "aaa" }
        )  

and it is possible (as expected) to change the main list, e.g.:

list.add(MutableList<Double>(4) { 0.0 }

and after this operation, the result of println(list) is as follows:

[[0, 0, 0], [aaa, aaa], [0.0, 0.0, 0.0, 0.0]]

BUT IT IS IMPOSSIBLE to change the sublists,
e.g. the operations:

list[0][1] = 2
list[1].add("bbb")

produce the error messages:
    The integer literal does not conform to the expected type Nothing
and
    Type mismatch: inferred type is String but Nothing was expected
respectively...

MY QUESTIONS ARE:
Why these nested sublists - declared as mutable - behave as they were immutable???
Is there any way to change these nested sublists???


By the way...
I've already checked, that nested sublists declared mutable, behave quite normal when they are all of the same type.
It is possible - as expected - to change those nested sublists in that case...


Solution

  • Since MutableList is typed you must let compiler know the exact type of sublist before adding item to it. Following code:

    (list[0] as MutableList<Int>)[1] = 2
    (list[1] as MutableList<String>).add("bbb")
    

    works well. However you can still have problems in runtime when you'll try to cast to a wrong MutableList type, that's why a warning ("Unchecked cast") is reported.

    When all sublists are of the same type e.g. Int the top-level list type is unambiguously inferred by compiler as MutableList<MutableList<Int>> so you don't have to explicitly tell compiler the type of sublists as it's already aware of them. But when sublists are of different types compiler infers list's type as MutableList<MutableList<Any>> and limits you to invoke only non-typed methods of MutableList (e.g. clear()) without casting.