Android Room возвращает значение NULL как ненулевой тип
Я имею Dao
вернуть простой объект. Если объект не существует, возврат комнаты null
, но приложение для Android не имеет сбоев. Также, если я назначу это значение переменной, не равной NULL, в приложении не будет сбоев.
Dao:
@Query("SELECT * FROM users WHERE id LIKE :id LIMIT 1")
abstract fun getById(id: Long): User
не сбой код:
doAsync {
val user: User = userDao.getById(999) // user 999 not exist, userDao returns null
uiThread {
if (user == null) {
Timber.d("user is $user") // "user is null" in log
} else {
Timber.d("user is ${user.email}")
}
}
}
У меня есть два вопроса:
- Как возможно, что Room может возвращать нулевое значение как ненулевую переменную?
- Как возможно, что код с присвоением нуля переменной, отличной от нуля, не имеет сбоев?
3 ответа
Это все о том, как Kotlin обрабатывает нули на границах с помощью Java-кода.
Если вы передадите null из Java в метод Kotlin, который требует ненулевое значение, вы получите исключение. Это достигается с помощью звонков Intrinsics.checkNotNull
функция, добавленная компилятором Kotlin в начале метода.
Например:
fun hello(who: String): Unit {
println ("Hello $who")
}
становится
public final void hello(@NotNull String who) {
Intrinsics.checkParameterIsNotNull(who, "who");
String var2 = "Hello " + who;
System.out.println(var2);
}
Аналогичная проверка добавляется при вызове методов Java из Kotlin.
Но в вашем случае у вас есть интерфейс Kotlin и его реализация в Java, сгенерированная Room. Таким образом, компилятор Kotlin не может добавлять проверки, потому что он не имеет контроля над всеми реализациями интерфейса. В противном случае ему придется добавлять проверки после каждого вызова класса или интерфейса Kotlin, потому что это может быть реализовано в Java, что плохо сказывается на производительности.
UPD: обнаружена похожая проблема на сайте отслеживания ошибок https://issuetracker.google.com/issues/112323132. Гуглер сказал, что это предполагаемое поведение, и если вы написали запрос, который может возвращать нулевое значение, вы обязаны пометить его как обнуляемый в интерфейсе dao.
Причина, по которой вы не получаете NullPointerException
прост: Room - это библиотека Java, а не Kotlin, поэтому он не имеет User
не должно быть нулевым. При взаимодействии с Java-кодом Kotlin вводит некоторые нулевые проверки, чтобы дать вам это исключение, но, поскольку ваш интерфейс написан на Kotlin, он предполагает, что это не будет проблемой, и пропускает проверку.
Я не эксперт по Room, но после быстрого поиска в Google я не смог найти способ заставить Room проверять наличие нулей. Я вижу два способа решения вашей "проблемы":
- Изменить
getById
функция для возвратаUser?
- Конвертировать ваши
@Dao
к интерфейсу Java, заставляя Kotlin проверить на нулевое значение.
Как я понимаю, ваш сгенерированный класс Dao не аннотировал возвращаемое значение как @Nullable в коде Java.
@Override
public User getById(long id) {
должно быть
@Override
public @Nullable User getById(long id) {
в результате вы не видите предупреждение