当房间为空时,Room with Flow 返回 null。

15

我刚开始接触Room、协程和Flow,发现了一个奇怪的问题:我预期为空的Flow实际上有一个空项。

我的设置如下,使用泛型T表示我的实际实体。

interface TDao {

    @Query("SELECT * FROM Table WHERE date=:date")
    fun getT(date: String): Flow<T>
}

@Singleton
class TRepository @Inject constructor(
    private val apiService: TApiService,
    private val Tdao: TDao
) {

    suspend fun getTFor(date: String): Flow<T> =
        Tdao
            .getT(date)
            .map {
                if (it == null) {
                    returnTFromDatabase()
                } else {
                    it
                }
            }

现在,当数据库中没有任何日期为T的数据时,我期望它返回一个空的流,其中没有任何项目。但实际上,它有一个null元素,这不应该发生,因为T不可为空。我为此编写了以下测试:
@RunWith(AndroidJUnit4::class)
class TDatabaseTest {

    private lateinit var db: TDatabase
    private lateinit var underTest: TDao

    @Before
    fun setUp() {
        val context = InstrumentationRegistry.getInstrumentation().context
        db = Room.inMemoryDatabaseBuilder(context, TDatabase::class.java).build()
        underTest = db.TDao()
    }

    @After
    fun tearDown() {
        db.close()
    }

    @Test
    fun givenEmptyDatabase_thenHasNoItems() {
        runBlocking {
            val list = underTest.getT("1999").take(1).toList()
            assertEquals(1, list.size)
        }
    }
}

......并且它通过了,因为再次返回了一个null项。

我错过了什么?这里出了什么问题,因为我无法完全弄清楚。为什么在非空元素的流中会得到一个单独的null元素?


据我上次检查,Room与泛型不兼容。此外,我建议您阅读Kotlin泛型。由于T没有上限,因此默认上限为Any?链接)。 - Giorgos Neokleous
我的代码中并不是通用的,我只是用 T 来代替了实际实体的提及。在我的代码中,T 是一个真实存在的实体。 - straphe
@GiorgosNeokleous一个给定的“T”可能是非空的,因此将“null”作为“T < Any?”传递永远不可以。你只需要记住“T < T?”即可。 - Marko Topolnik
2个回答

22

Room是一个用Java编写的数据库,因此它忽略了Kotlin的可选项。我建议始终声明查询返回类型或者在您的情况下使用Flow<T?>类型来表示可选项。如果您不想在流中使用空类型,可以像这样使用filterNotNull()函数:

Tdao.getT(date).filterNotNull()

22
如何处理空值取决于查询函数的返回类型。文档中提到:
  • 当返回类型为Flow<T>时,查询空表会抛出空指针异常
  • 当返回类型为Flow<T?>时,查询空表会发出一个null值。
  • 当返回类型为Flow<List<T>>时,查询空表会发出一个空列表
上述代码片段讨论了空表,但我认为相同的行为适用于任何未返回行的查询。
来源:Room查询文档

4
文档上说它会抛出空指针异常,但实际上我在我的流程中也收到了一个空值。 - Learn OpenGL ES
2
已经报告了一个差异:Room Coroutine和Flow @Query不能按预期处理null。截至本文撰写时,似乎仍然未解决。提供的解决方案是切换到KSP或声明查询返回Flow<T?>并在需要时进行过滤(正如Pierluigi在他的答案中建议的那样)。 - big_m

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接