Android Room返回Null作为Non-Null类型

Fra*_*cis 5 crash android kotlin android-room kotlin-null-safety

我必须Dao返回简单的对象。如果对象不存在,则Room返回null,但是Android应用程序没有崩溃。另外,如果我将该值分配给非null变量,则应用程序中不会发生崩溃。

道:

@Query("SELECT * FROM users WHERE id LIKE :id LIMIT 1")
abstract fun getById(id: Long): User
Run Code Online (Sandbox Code Playgroud)

不崩溃的代码:

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}")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我有两个问题:

  1. Room如何将Null值作为Non-Null变量返回?
  2. 将null分配给Non-Null变量的代码如何不会崩溃

Kur*_*soR 6

这全都涉及Kotlin如何使用Java代码处理边界上的空值。

如果您将Java中的null传递给需要非null值的Kotlin方法,则会获得异常。这是通过调用Intrinsics.checkNotNullKotlin编译器在方法开始时添加的函数来实现的。

例如:

fun hello(who: String): Unit {
    println ("Hello $who")
}
Run Code Online (Sandbox Code Playgroud)

变成

public final void hello(@NotNull String who) {
    Intrinsics.checkParameterIsNotNull(who, "who");
    String var2 = "Hello " + who;
    System.out.println(var2);
}
Run Code Online (Sandbox Code Playgroud)

从Kotlin调用Java方法时,添加了类似的检查。

但是如果您有Kotlin接口,它是由Room生成的Java实现。因此Kotlin编译器无法添加检查,因为它无法控制所有接口实现。否则,它将在每次调用Kotlin类或接口之后添加检查,因为它可以用Java实现,这对性能不利。

UPD:在Room bugtracker https://issuetracker.google.com/issues/112323132上发现了类似的问题。Googler表示,这是预期的行为,如果您编写的查询可以返回空值,则您有责任在dao接口中将其标记为可空值。


小智 5

你没有得到 a 的原因NullPointerException很简单:Room 是一个 Java 库,而不是 Kotlin,所以它不知道你User不应该为 null。与 Java 代码交互时,Kotlin 引入了一些 null 检查来给您提供该异常,但由于您的接口是用 Kotlin 编写的,因此它认为这不会成为问题并跳过检查。

我不是 Room 专家,但经过快速谷歌搜索后,我找不到强制 Room 检查空值的方法。我看到解决你的“问题”的两种方法:

  • 更改getById函数返回值User?
  • 将您的接口转换@Dao为 Java 接口,强制 Kotlin 检查 null。