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?
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]
}