Изменяемые данные Android должны использовать только обнуляемые объекты?

#android #kotlin #android-livedata #mutablelivedata #non-nullable

Вопрос:

Мой упрощенный код таков:

 class MyViewModel : ViewModel() {
    private val myLiveData: MutableLiveData<MyObject> = MutableLiveData<MyObject>(MyObject())
    val myObject: MutableLiveData<MyObject>
        get() = myLiveData

    fun addItem(item: MyItem) {
        val value = myLiveData.value // value is of nullable type MyObject?
        // MyObject contains a list of other objects
        (value?.myList as MutableList).add(item)
        myLiveData.value = value!! // Here I get a IDE warning or error
    }
}
 

Если я оставлю ненулевой оператор (!!) в строке 9, Android Studio выдаст мне предупреждение:

 Unnecessary non-null assertion (!!) on a non-null receiver of type MyObject
 

Если я удалю ненулевой оператор (!!) в строке 9, Android Studio выдаст мне ошибку:

 Expected non-nullable value
 

Как правильно поступить с этим в Котлине? Если я использую nullable MyObject? в строках 2 и 3, Android Studio, похоже, это нравится, но на самом деле я не собираюсь, чтобы эти данные были нулевыми… MutableLiveData Предполагается, что он всегда может быть аннулирован?

Комментарии:

1. можете ли вы добавить код для MyItem и MyObject также здесь, чтобы правильно понять вашу проблему.

2. Если вы следуете рекомендациям, то нет смысла создавать MutableLiveData, а MutableLiveData не должны содержать объект, обнуляемый.

3. Вам нужны изменяемые данные, чтобы задать для них значения, и они могут быть обнулены, если вы хотите, чтобы они содержали именно такие данные. Вы не должны выставлять его как MutableLiveData тип myObject , хотя — тип должен быть просто LiveData

Ответ №1:

LiveData действительно предназначено для наблюдения — когда вы вызываете observe его и передаете функцию обработчика, эта функция будет вызываться только тогда, когда у LiveData есть значение (текущее или новое). Поскольку ваши данные LiveData содержат ненулевой MyObject тип, функции наблюдателя будут присвоены только ненулевые MyObject s.

Но если вы копаетесь в его value собственности — это другое дело! Даже если вы указываете начальное MyObject значение в его конструкторе, вам не нужно этого делать — поэтому LiveData s иногда может быть пустым. Если вы попытаетесь это заметить, наблюдатель не будет вызван до тех пор, пока не будет установлено значение. Если вы попытаетесь посмотреть на него value , это будет так null , потому что в живых данных еще ничего нет. Это не значит, что он предоставляет наблюдателям нули, просто у него еще не сохранено значение!

So value всегда может быть обнулен, даже если тип LiveData , который содержит, ненулевой. Ты как бы заглядываешь за кулисы. На самом деле у вас есть два варианта:

  • используйте нулевую безопасность обычным способом ( value?.let { ... } ) и не беспокойтесь об этом, потому что вы знаете, что там всегда будет значение, потому что вы установили его в конструкторе (это самый безопасный вариант).
  • используйте !! , потому что это, кажется, одна из немногих ситуаций, когда это безопасно — вы знаете, что это не будет равно нулю

Лично я думаю, что все это довольно грязно, LiveData Хас… причуды, но ты в порядке в этой ситуации — я просто хотел объяснить, что происходит!


Также ваш общедоступный тип должен быть LiveData<MyObject> и не MutableLiveData<MyObject> должен — ваша внутренняя копия должна быть изменяемой (чтобы вы могли ее обновить), но ссылка, которую вы предоставляете, должна быть неизменяемым LiveData типом, чтобы пользователи не могли ее установить и должны были пройти через вашу функцию настройки. Просто измените тип:

 val myObject: LiveData<MyObject>
        get() = myLiveData
 

и они будут рассматривать это как стандарт LiveData , с которым они не могут связываться, вместо изменяемого подкласса

Комментарии:

1. Ты уверен, что все не наоборот? Возможно, я неправильно помню, но я думаю, что LiveData не поддерживает ненулевую возможность. Значение Null всегда принимается в качестве допустимого значения, которое будет опубликовано наблюдателям, поэтому в основном он не знает, что вы объявили тип как ненулевой, и всегда рассматривает тип как обнуляемый.

2. Вы можете переместить значение null в данные LiveData ненулевого типа, но (по крайней мере, в Котлине) произойдет сбой с it must not be null NPE-возможно, когда произойдет приведение к ненулевому типу. Но вы правы, это не применяется во время компиляции или что-то в этом роде, и линтеру все равно. Но до тех пор, пока вы используете (ненулевой) установщик, через который все должно проходить, ваши наблюдатели никогда не должны получать null то, что им нужно обрабатывать, даже если value оно равно нулю, потому что вы еще не установили начальное значение-вот к чему я клоню. Вы, конечно, можете сломать его несколькими интересными способами, хотя!