Почему значение 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 шт):
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) }
}
}
Сахар в том, что по сути, мутабельность не выноситься наружу (по крайней мере, легально ты не сможешь получить доступ к мутабельной лайвдате).