Android Room возвращает значение NULL как ненулевой тип - PullRequest
0 голосов
/ 07 ноября 2018

У меня есть Dao для возврата простого объекта. Если объект не существует, Room возвращает null, но приложение Android не имеет сбоев. Также, если я назначу это значение переменной, не равной NULL, в приложении не произойдет сбоев.

ДАО:

@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 ]

0 голосов
/ 07 ноября 2018

Причина, по которой вы не получаете NullPointerException, проста: Room - это библиотека Java, а не Kotlin, поэтому она даже не подозревает, что ваш User не должен быть нулевым. При взаимодействии с кодом Java Kotlin вводит некоторые нулевые проверки, чтобы дать вам это исключение, но, поскольку ваш интерфейс написан на Kotlin, он предполагает, что это не будет проблемой, и пропускает проверку.

Я не эксперт по Room, но после быстрого поиска в Google я не смог найти способ заставить Room проверять наличие нулей. Я вижу два способа решения вашей «проблемы»:

  • Измените функцию getById, чтобы вернуть User?
  • Конвертируйте ваш @Dao в интерфейс Java, заставляя Kotlin проверять наличие нуля.
0 голосов
/ 07 ноября 2018

Это все о том, как 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: обнаружена похожая проблема на Room bugtracker https://issuetracker.google.com/issues/112323132. Гуглер сказал, что это целевое поведение, и если вы написали запрос, который может возвращать нулевое значение, то вы обязаны пометить его как обнуляемый в интерфейсе dao.

0 голосов
/ 07 ноября 2018

Как я понимаю, ваш сгенерированный класс Dao не аннотировал возвращаемое значение как @Nullable в коде Java.

@Override
public User getById(long id) {

должно быть

@Override
public @Nullable User getById(long id) {

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

...