How to really check if a List is Mutable?


#1

Is there any way to really check if a list is mutable?

Casting seems not to work as you can see in this example:

fun main() {
    val list: List<Int> = listOf(1, 2, 3)
    (list as? MutableList<Int>)?.add(42)
}

However, using full reflection I have managed to check it a list is mutable:

import kotlin.reflect.full.allSupertypes

fun main() {
    val l0: List<Int> = emptyList()
    val l1: List<Int> = listOf()
    val l2: List<Int> = mutableListOf()
    val l3: List<Int> = arrayListOf()
    val l4: List<Int> = java.util.ArrayList()
    val l5: List<Int> = java.util.LinkedList()

    l0.isMutable() // false
    l1.isMutable() // false
    l2.isMutable() // true
    l3.isMutable() // true
    l4.isMutable() // true
    l5.isMutable() // true
}

private fun List<*>.isMutable() {
    println(this::class.allSupertypes.any { it.toString() == "kotlin.collections.MutableList<E>" })
}

But it doesn’t look pretty and reflection has a high cost in terms of performance.

So, is there any other way to check if a list is mutable?


#2

Just use the type checker refName is ClassName. Most, if not all, mutable List implementations extend MutableList. Or they should…

fun main() {
    val l0: List<Int> = emptyList()
    val l1: List<Int> = listOf()
    val l2: List<Int> = mutableListOf()
    val l3: List<Int> = arrayListOf()
    val l4: List<Int> = java.util.ArrayList()
    val l5: List<Int> = java.util.LinkedList()

    for (list in arrayOf(l0, l1, l2, l3, l4, l5)) {
        println(list is MutableList)
    }
}

prints what you are expecting


#3

I don’t think there is any way except reflection that works. The reason casting doesn’t work (as I understand it) is that List and MutableList don’t actually exist at runtime at least for classes like ArrayList. There is some magic there, which makes both of those interfaces compile to java.util.List. This is why your read only list seems to implement MutableList.
The way java “solved” read only lists is by just having the read-only lists throw UnsupportedOperationExceptions.

Reading the question helps. He just showed that this does not work :man_facepalming: Try listOf(1, 2, 3).


Looking at the generated byte-code for list is MutableList I noticed that there is a special function in kotlin.jvm.internal.TypeIntrinsics used to do this.
Here is it’s current definition

public static boolean isMutableList(Object obj) {
    return obj instanceof List &&
           (!(obj instanceof KMappedMarker) || obj instanceof KMutableList);
}

As far as I understand it, this function assumes that all lists implemented in Java are mutable. I guess KMutableList and KMappedMarker get used (by the kotlin compiler) to mark whether list implementations are mutable or not.
So I guess the real problem is how to check that java-lists are mutable and I don’t think there is.


#4

Huh, this actually isn’t what I expected to happen, I just kind of assumed that listOf returned just a read only List because thats what the signature is for listOf and Array.asList(). Interesting, it seems most if not all lists are compiled to ArrayList


#5

I am surprised that the reflection solution works. Those Kotlin collection types don’t exist at runtime. :thinking:


#6

There is no way, as the Java List implementations don’t show if they’re immutable or not.

If there were an ImmutableList interface (like the one of the Guava collections), you could just do is ImmutableList, but as it it, with Kotlin that doesn’t work (obviously unless you use Guava in Kotlin).


#7

The only way is to cast to MutableList and modify the collection. If you’ve got UnsupportedOperationException, then the collection was unmodifiable. At least if you care about Java interop. Your code won’t work with Collections.unmodifiableList, for example.


#8

This could be used after some study as a last resort, but I’m not a fan of it as it would alter the collection being checked.

In fact, it does work surprisingly.

import kotlin.reflect.full.allSupertypes

fun main() {
    val l1: List<Int> = java.util.ArrayList()
    val l2: List<Int> = java.util.Collections.unmodifiableList(java.util.ArrayList())

    l1.isMutable() // true
    l2.isMutable() // false
}

private fun List<*>.isMutable() {
    println(this::class.allSupertypes.any { it.toString() == "kotlin.collections.MutableList<E>" })
}

#9

Probably you want to alter it, why else would you want to check? But yes, not general-purpose solution, of course.

That’s surprising indeed, I was sure that it wouldn’t work. Probably they have some kind of white-list for unmodifiable collection types. I wasn’t able to find that code from the first glance, but it should be somewhere and may be it’s possible to reuse it. My point is that in Java the only “marker” for unmodifiable collection is to throw exception, so other than white-listing known unmodifiable collections, there’s no way to tell if some particular class modifiable or not. Well, you could try to extract bytecode for something like add method and patern-match it :slight_smile: Just a crazy thought.

Anyway if your Kotlin code works well enough and you’re sure that its performance is bad, just use cache for this::class. Something like Map<KClass, Boolean?>.


#10

After some more testing, I have concluded that this check only returns true for instances of java.util.ArrayList or java.util.LinkedList:

fun List<*>.isMutable() = this::class.allSupertypes.any { it.toString() == "kotlin.collections.MutableList<E>" }

This means that you can check if a list is mutable by checking if it is an ArrayList or a LinkedList.
However, if it is of one of the two types it means it is mutable but if it is not it may or may not be mutable.

For example, the com.google.common.primitives.Ints.IntArrayAsList wraps an array of ints while allowing to set the elements at each position, this list is not an instance of ArrayList or LinkedList but it is mutable.


#11

There was similar question asked on StackOverflow a few months ago, which @yole answered:

The separation between List and MutableList is an illusion created by the Kotlin compiler. At runtime, Kotlin uses Java collection classes, which only have a single List interface containing both read and mutation methods. Compile-time references to List and MutableList are both compiled to java.util.List references. Therefore, it’s not possible to detect whether a list is a MutableList at runtime.