【问题标题】:Android Room returns Null as Non-Null typeAndroid Room 将 Null 作为非 Null 类型返回
【发布时间】:2019-04-10 17:36:27
【问题描述】:

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

道:

@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 怎么可能将 Null 值作为非 Null 变量返回?
  2. 将 null 分配给 Non-Null 变量的代码怎么可能没有崩溃

【问题讨论】:

  • 你能重复你的第一个问题吗?你其实是在自相矛盾。你问:How is possible that Room can return Null value as Non-Null variable?,但你的评论说user 999 not exist, userDao returns null
  • @ZUNJAE 我的观点是函数getById() 具有非空返回值(User),但它返回空值。在那种情况下,我预计NullPointerException
  • 这是因为 Room 的底层代码返回一个可为空的值,但在 Kotlin 中,您强制它不可为空,这会导致 TypeCastException。我个人不知道解决方案,因为我现在有同样的问题。我只是将所有方法都注释为可为空的。

标签: android kotlin crash android-room kotlin-null-safety


【解决方案1】:

这完全是关于 Kotlin 如何处理 Java 代码边界上的空值。

如果您将 null 从 Java 传递给需要非 null 值的 Kotlin 方法,您将得到异常。这是通过调用 Kotlin 编译器在方法开头添加的 Intrinsics.checkNotNull 函数来实现的。

例如:

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

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

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

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

【讨论】:

  • 感谢您的挖掘:“如果您编写的查询可以返回空值,那么您有责任在 dao 接口中将其标记为可空”。该规则似乎也适用于LiveData<T?>
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    据我了解,您生成的 Dao 类没有在 Java 代码中将返回值注释为 @Nullable。

    @Override
    public User getById(long id) {
    

    应该是

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

    因此您没有看到警告

    【讨论】:

    • 我的代码是用 Kotlin 编写的,Dao 类的返回值不能为空。所以我希望 NullPointerException 在 null 的情况下,但我没有收到。
    • 是的,但是如果你检查 byteCode 并反编译它,在 Android studio Cmd + Shift + A 中写入bytecode,然后点击按钮反编译
    猜你喜欢
    • 2011-08-21
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    • 2023-01-10
    • 1970-01-01
    • 2020-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多