0

It seems Kotlin core Data Structures (i.e. Map, List, Set) are really an interface and not really immutable.

If I have:

  fun <K,V> foo(map: Map<K, V>) {
    ...
  }

Can map change after I received it - from outside?

In which cases is it possible?

Lior Bar-On
  • 8,250
  • 3
  • 28
  • 41
  • Possible duplicate of [Kotlin and Immutable Collections?](https://stackoverflow.com/questions/33727657/kotlin-and-immutable-collections) – Eamon Scullion Sep 20 '19 at 10:45

3 Answers3

3

No.

The Map interface does not provide any mutator methods, so an implementation could be completely immutable.

But other implementations aren't — in particular, MutableMap is a subinterface, so anything implementing that is a mutable Map.  This means that code with a reference to a Map could potentially see the data changing, even though it couldn't make those changes itself.

Similarly, MutableList is a subinterface of List, and MutableSet is a subinterface of Set.

There are immutable implementations of those top-level interfaces (such as the kotlinx.collections.immutable and Guava libraries) — and you could write your own.  But the Kotlin language and type system don't yet provide strong support for deep immutability, only for read-only interfaces to data that may or may not be immutable.

(That's not to say that such support couldn't be added in future.  There is a lot of interest in it, and JetBrains have been considering it.)

gidds
  • 9,862
  • 1
  • 12
  • 16
2

Let's run an experiment:

class Foo {

    @Test
    fun foo() {
        val items = mutableListOf("A")
        run(items)
        Thread.sleep(1000)
        items.add("B")
        println("Foo")
        Thread.sleep(2000)
    }

    fun run(items: List<String>) {
        thread(start = true) {
            println("Run ${items.count()}")
            Thread.sleep(2000)
            println("Run ${items.count()}")
        }
    }
}

This test case will create a mutable list of 1 item, it will then pass a reference to this list into a method whose type is for an immutable list.

This method called run will diplay the length of the list.

Outside of the run method a new item will be appended to the list.

sleeps have been added ensure that the addition to the list happen after run's first statement but before the second print statement.

Let's examine the output:

Run 1
Foo
Run 2

As we can see, the list contents did indeed change, even though run took in an immutable list.

This is because MutableList and List are merely interfaces and all MutableList implementations also implement List.

When Kotlin refers to mutable and immutable it simply references whether the methods to modify the collection are present, not whether the contents can be changed.

So if you take in a list to a method using List as the parameter type then yes, the contents can vary if they are altered by another thread, if that is a concern then make a copy of the list as the first thing your method does.

Keith Kirk
  • 256
  • 2
  • 15
0

As other have indicated, the map could be modified while you're using it in another thread... however that would already be broken unless your access to the map was @Synchronized, which would indicate that you knew it would change, so this possibility is not really a problem. Even if your method took a MutableMap parameter it would be wrong if it was changed while your method was in progress.

I think you're misinterpreting the purpose of the read-only collection interfaces.

When your method accepts a Map as a parameter, you are indicating that the method will not change the map. The purpose of the read-only Map interface is to allow you to say such things. You could do (map as? MutableMap)?.put(...), but that would be wrong since you promised not to do that. You could also crash the process in various ways or run an infinite loop, but that would also be wrong. Just don't do it. The language does not provide protection against malicious programmers.

Similarly, if your method returns a Map, that indicates that the receiver must not change it. Usually in such cases, you also promise (hopefully in a comment) that the returned map will not change. You can't keep this promise if anyone who receives the map can change it themselves, and that is why you return the Map instead of the underlying MutableMap

Matt Timmermans
  • 36,921
  • 2
  • 27
  • 59
  • It doesn't need to be from a different thread; it could be on the current threat, via a callback or getter or some other code you're not aware of calling. – gidds Sep 20 '19 at 19:23