Почему значение immutable переменных иногда можно менять?

Внизу пример где значение LiveData объекта меняется хотя везде сказано что значение immutable нельзя менять. Объясните что я не так понимаю.

    class RestaurantViewModel @Inject constructor(
    api: RestaurantApi
) : ViewModel() {
        private val restaurantsLiveData = MutableLiveData<List<Restaurant>>()
        val restaurants: LiveData<List<Restaurant>> = restaurantsLiveData //та часть которую я не понимаю. 
//то есть мы можем менять значение MutableLiveData и присваивая это значение по сути делаем LiveData меняемым?
    init {
        viewModelScope.launch {
            val restaurants = api.getRestaurants()
            delay(2000)
            restaurantsLiveData.value = restaurants
        }
    }
}

И почему нельзя было придумать что-то, что позволяло бы не создавать MutableLiveData а сразу передавать данные в LiveData?


Ответы (1 шт):

Автор решения: Vadim Yaroschuk

val указывает переменной, что ссылка на объект неизменяемая. Но, с учётом одного из свойств ООП - инкапсуляции (объект может изменять своё содержимое изнутри), значение там не одинаковое.

MutableLiveData - как уже отметили, сама за себя говорит - я изменяюсь. Но не сама MutableLiveData изменяется, а её содержимое в классе, которое оно возвращает.

Более простой вариант инкапсуляции:

// делаем в классе мутабельную переменную
class Foo(var value: String)

object Bar {
   // создаём переменную, где не меняется ссылка
   val foo: Foo = Foo("something")
}

fun main() {
   // сохраним что-бы потом проверить одинаковые ли объекты
   val preEditedValue = Bar.foo

   // значение меняется
   Bar.foo.value = "Test"
   // проверяем на Referential equality
   println(preEditedValue === Bar.foo)
}

Суть в том, что-бы сокрыть мутабельность данных для публичного доступа (например, в вашем примере View ничего не должна изменять напрямую в MutableLiveData) туда и отдаётся LiveData, что не даёт возможности изменять данные там, где этого быть и не должно. Это не что-то новое, например, в Kotlin'е над этим очень сильно задумались, начиная с коллекций - MutableCollection, Collection, заканчивая аналогом LiveData - MutableStateFlow, StateFlow.

Мутабельность никогда не должна выноситься в публичный доступ во избежания проблем. Контролировать поток данных проще в одном классе.

Возможное решение (я так например делаю)

Для ViewModel, я создаю абстрактный класс, где определено всё, что будет выноситься наружу:

abstract class MyViewModel : ViewModel() {
   abstract val list: LiveData<List<String>>

   abstract fun addItem(item: String)
}

Потом создаю "интегрированную" версию ViewModel'и (обычно делаю её private):

fun MyViewModel(): MyViewModel = IntegratedMyViewModel()

private class IntegratedMyViewModel : MyViewModel() {
   override val list: MutableLiveData<List<String>> = MutableLiveData(listOf())
   override fun addItem(item: String) {
      list.value = list.value.toMutableList().apply { add(item) }
   }
}

Сахар в том, что по сути, мутабельность не выноситься наружу (по крайней мере, легально ты не сможешь получить доступ к мутабельной лайвдате).

→ Ссылка