Not sure what the best channel for this is, but it...
# language-proposals
n
Not sure what the best channel for this is, but it’s a compiler issue, so language-proposals seems the closest fit… In Java 11, the functions
List.of(…)
and
List.copyOf(…)
create immutable lists. The Kotlin equivalents,
listOf(…)
and
anIterable.toList()
actually create mutable lists but return the List interface that doesn’t allow mutation. In Kotlin, that’s not a problem — lists are processed by being transformed with map/flatMap/filter/etc. and so nothing holds on to a reference to mutable lists and aliasing errors do not occur. However… when you pass the Kotlin List across the Java interop boundary, Java sees it as a mutable java.util.List and, because the Kotlin list is actually mutable, Java can mutate a list through an interface that the Kotlin type system considers to be unmodifiable. Worse, the Kotlin code is now more error prone than the Java code, because the Java code would fail immediately if you tried to mutate the immutable list, but the Kotlin code allows that list to be mutated. Suggestions: • when the kotlin compiler is targeting JVM version 11 and above, translate Kotlin’s listOf and toList to the JDK List.of and List.copyOf functions. • on 1.8 and below, wrap Kolin Lists with Collections.unmodifiableList (or some equivalent in the Kotlin runtime) when being passed across the interop boundary.
👍 3
j
I'm not sure about the second point, but the first was on my to-do list. It's pretty easy to write these as a compiler intrinsic. Also you only need to target 9 for List.of, not 11.
n
👍
i
Note that
List.of
prohibits nullable values, and `Set.of`/`Map.of` additionally prohibit duplicate elements/keys.
wrap Kolin Lists with Collections.unmodifiableList (or some equivalent in the Kotlin runtime) when being passed across the interop boundary.
This would change their identity making the interop less transparent.
n
How about providing an actually immutable implementation of the List interface? https://github.com/Kotlin/kotlinx.collections.immutable I always thought that this proposal meant to fill that gap.
i
We currently have the experimental collection builders:
buildList
,
buildSet
,
buildMap
. The collections returned by them are frozen, so they cannot be mutated even with downcast and even from java. We have some thoughts about using these collection implementations for results of collection operations, such as map,filter, etc, but the effect of this change still has to be carefully evaluated.
👍 7
z
would kotlinx.collections.immutable just be focused on offering more performant lists rather than immutable ones in that case? I'm curious what the longer term value prop is over the collection builders (if any) for average usage
j
They're only more performant for particular workloads. In other workloads they're basically memory leaks.
d
It turns out that Kotlin
listOf(…)
returns a List that can be downcast to
MutableList
but which is actually immutable at runtime
Copy code
val aList: List<String> = listOf("0", "1")
val aMutableList: MutableList<String> = aList as MutableList<String>
aMutableList.removeAt(1) // throws UnsupportedOperationException
In general though,
List
cannot be downcast to
MutableList
Copy code
class MyList<T>(vararg items: T): List<T> by items.toList()

val aList: List<String> = MyList("0", "1")
val aMutableList: MutableList<String> = aList as MutableList<String> // throws ClassCastException
Sigh. That result of
listOf(...)
is mutable, you just can’t change its size.
Copy code
val aList: List<String> = listOf("0", "1")
val aMutableList: MutableList<String> = aList as MutableList<String>
aMutableList.set(1, "banana")
assertEquals(listOf("0", "banana"), aMutableList)
j
With sufficient reflection you can!
d
With sufficient reflection I can make my strings mutable too 😱