androidkotlinimmutabilitymutablebacking-field

Kotlin backing field wrong type


ViewModel

private val _wordPressPostsState = Channel<WordPressPostsState>()

val wordPressPostList: List<WordPressPostDataDomain>
    field = mutableListOf<WordPressPostDataDomain>()

Inside a function that is calling a use case which returns a Flow

when (it) {

    is RequestStatus.Success -> {
       if (it.data.postList.isNotEmpty()) {
          wordPressPostList.addAll(it.data.postList)
       }
       _wordPressPostsState.send(WordPressPostsState.FetchSuccess(wordPressPostList.toList()))
    }

}

Fragment

val newsAdapter: ListAdapter

newsAdapter.submitList(state.postList)

ListAdapter won't work without calling .toList() on either state.postList or wordPressPostList when passing it to state WordPressPostsState.FetchSuccess in ViewModel. This means that wordPressPostList is still a MutableList.

One thing I noticed is that the data type was shown to be both List inside and outside but actually behaves like mutable in ViewModel and immutable in Fragment similar of what is stated in the documentation.


Solution

  • ListAdapter demands a new list instance each time you call submitList(). It doesn’t matter if the list instance is mutable or not, although you probably will not be using mutable lists with it precisely because it needs a new instance each time and will break if you ever modify a list after passing it to submitList().

    toList() makes your code (kind of) work because it creates a new list instance, not because it creates a read-only list. However, after you expand what you are doing, you will still have broken behavior because you are modifying the list after submitting it.

    Without using toList(), you are passing it the same list instance each time, so that wouldn’t work. When you pass a ListAdapter the same list instance you passed previously, then when it tries to compare the old and new lists, it sees that they are identical, so it doesn’t react.

    Also, although it isn’t relevant in this case, you do have a misunderstanding about upcasting. A property with explicit backing field upcasts the field value to the type of the property. So in this case a MutableList is upcast to a List. This doesn’t mean the List isn’t mutable anymore. It’s still the same instance/reference of a MutableList. Upcasting means that code that references it through the property doesn’t know that it’s a MutableList and won’t be able to directly mutate it, but it still is a MutableList.