Issue
Consider following Kotlin code:
data class CachedUserData(var contracts: MutableList<String>) {
var localDateTime: LocalDateTime = LocalDateTime.now()
}
When I'm trying to get via auto-generated getter contracts list and modify it from outside, I'm getting exception thrown java.lang.UnsupportedOperationException: null. Investigation shows, that in this class the contracts field is being initialized to type ImmutableCollections$ListN.
I'm able to bypass this by adding following data modification function:
fun addContract(item: String) {
val newList = mutableListOf(*contracts.toTypedArray());
newList.add(item);
contracts = newList;
}
then, this type directly after this class construction is ImmutableCollections$ListN, but after it's modification via this function call is changed to ArrayList and stays that way.
The instance of this particular class is created as a result of network resource fetch (plain Java List), that is afterwards being passed by stream to some filtering, and afterwards is being collected by .toList() Java collector.
My questions would be:
- Why initial type is set to ImmutableCollection, even I'm trying to strongly persuade Kotlin that I'd love to have this list to be modifiable?
- Is there a way to make it initially make them modifiable and use simple construct of
.getContracts().add(value);? - Is there a place, where such behavior is described in details? I.e. some implicit collections conversions to some immutable shadowing types?
Edit:
This class is instantiated in some Java part of project, and it's supplied with List<String> type as input parameter:
Many thanks in advance!
Solution
Java does not have an extra type to distinguish mutable lists from immutable lists. All lists, mutable or immutable are represented by the java.util.List type. In Kotlin, both List and MutableList are mapped to java.util.List interface in Java. See the mapping table here.
Even if you say your class' constructor should take a MutableList in Kotlin, the constructor takes a java.util.List as far as Java is concerned. Therefore, code in Java can pass both mutable and immutable lists to it.
You said that the Java code is passing the result of a Stream.toList call to the constructor, and that is the problem - toList is designed to return an immutable list:
The returned
Listis unmodifiable; calls to any mutator method will always causeUnsupportedOperationExceptionto be thrown.
You should instead use Collectors.toCollection to control the type of the list that is returned, and pass in a supplier of a mutable list type.
// e.g. replace .toList() with
.collect(Collectors.toCollection(ArrayList::new))
Alternatively, instead of making a mutable copy of the list in addContract, you can do this in an init block instead:
data class CachedUserData(var contracts: MutableList<String>) {
init {
contracts = ArrayList(contracts)
}
}
Answered By - Sweeper
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.