23

I have a Java/Kotlin interop problem. A Kotlin immutable list is compiled into a normal java.util.ArrayList that is mutable!

Kotlin (library):

class A {
  val items: List<Item> = ArrayList()
}

Java (consumer):

A a = new A();
a.getItems().add(new Item());   // Compiles and runs but I wish to fail or throw

How to make my Kotlin class fully immutable from Java perspective too?

4
  • I'm not familiar with Kotlin, but the guava Java library has an ImmutableList type which you could use to wrap the mutable ArrayList. Commented Nov 12, 2016 at 15:45
  • 1
    unmodifiableList(ArrayList()) Commented Nov 12, 2016 at 16:05
  • 1
    Works. Post it as an answer please. Commented Nov 12, 2016 at 16:32
  • 1
    Possible duplicate of Kotlin and Immutable Collections? Commented Nov 12, 2016 at 19:43

2 Answers 2

29

All non-Mutable____ collections in Kotlin are compile time read-only types by default, but not immutable. See the following code snippet:

fun main(args: Array<String>) {
  // Explanation for ArrayList(listOf()) later down the post
  val list: List<Int> = ArrayList(listOf(1, 2, 3))
  println(list) // [1, 2, 3]

  // Fails at compile time
  // list.add(4)

  // Uh oh! This works at runtime!
  (list as MutableList<Int>).add(4)
  println(list) // [1, 2, 3, 4]
}

To truly have an immutable list, consider Guava's Immutable____ collections:

import com.google.common.collect.ImmutableList

fun main(args: Array<String>) {
  val list: List<Int> = ImmutableList.of(1, 2, 3)
  println(list) // [1, 2, 3]

  // Fails at compile time
  // list.add(4)

  // Fails at runtime, as expected
  (list as MutableList<Int>).add(4)
  println(list) // [1, 2, 3, 4]
}

Be aware that some of Kotlin's standard runtime function may return collections that are either unmodifiable, not resizable, etc., so all bets are off when you directly cast a read-only collection to a mutable one.

For example, listOf() currently (this may change in the future!) returns a java.util.Arrays.ArrayList around the array of vararg parameters via Arrays.asList(T...). This "list" can be modified, but elements can never be added or removed, as you cannot resize an array. See Arrays.asList(T...) javadoc for more information.

If you really want a mutable collection from any given collection, consider making a copy using .toMutableList(). This will work on any collection:

import com.google.common.collect.ImmutableList

fun main(args: Array<String>) {
  val list: List<Int> = ImmutableList.of(1, 2, 3)

  val copy = list.toMutableList()
  copy.add(4)

  println(copy) // [1, 2, 3, 4]
  println(list) // [1, 2, 3]
}
Sign up to request clarification or add additional context in comments.

2 Comments

Guava has to be avoided on Android. Collections.unmodifiableList() is the safest and simplest solution.
@WindRider Guava can be used with Android just fine but ProGuard is recommended
1

There is ImmutableList.copyOf(list) that returns an ImmutableList. This is Kotlin API, and you won't be able to use it in Java.

Another option is Collections.unmodifiableList from java.util, when used in Kotlin, it returned a mutable list type, but just like in Java, you will get an exception if you attempt to modify it.

2 Comments

Does this still exist in the stdlib?
kotlinx.collections.immutable provides immutable collections (i.e. methods like List.toImmutableList())

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.