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}")
        }
    }
}

У меня есть два вопроса:

  1. Как возможно, что Room может возвращать нулевое значение как ненулевую переменную?
  2. Как возможно, что код с присвоением нуля переменной, отличной от нуля, не имеет сбоев?

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) {

в результате вы не видите предупреждение

Другие вопросы по тегам