0

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.

5
  • 1
    It's hard to get what you mean. We don't know what is state, newsAdapter, _wordPressPostsState and others. Also, property definition for wordPressPostList isn't at all a valid Kotlin code. Commented Jul 2, 2024 at 20:17
  • Regarding the field definition - sorry, I didn't recognize a new feature. Commented Jul 2, 2024 at 21:33
  • What version of Kotlin are you using? I thought the so-called "explicit backing field" feature was still in discussion. Commented Jul 3, 2024 at 2:34
  • Ah, never mind. Didn't realize a prototype of the feature has been available since Kotlin 1.7 with the K2 compiler (which became the default compiler in Kotlin 2.0). Commented Jul 3, 2024 at 2:42
  • @broot I updated the question adding Android tags as well for those who are not very familiar on the pattern. state is your typical sealed class, newsAdapter is Android's ListAdapter, _wordPressPostsState is Kotlin's Channel. Commented Jul 3, 2024 at 3:52

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

In practice, I think it will be rare the backing fields property will make sense to be used with MutableList/List. It is just not common to pass around read-only Lists between classes while they are still being modified. That’s fragile code. It will be much more common to see backing fields used with MutableStateFlow and StateFlow or Flow. We always expect Flows to be emitting values and therefore mutating even though our reference is read-only. And Flows are designed to be used this way safely, unlike Lists, which will break if read while another thread writes to them.
Thanks a lot, didn't noticed it's just doing an upcasting there. The requiring of new instance of list for ListAdapter is also new to me.

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.