I've just started looking at Room, Coroutines, and Flow, and have come across something odd: what I'm expecting to be an empty flow actually has one null item in it.
My setup is as follows, with generic T
for my actual entities.
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
}
}
Now, when the database doesn't have any T
in it for date
, I'm expecting it to return an empty flow, with no items in it. Instead, it has one null
element, which should never happen, because T
isn't nullable.
I wrote this test for it:
@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)
}
}
}
...and it passes, cause, again, there's one null
item returned.
What am I missing? What's wrong here, because I can't quite figure it out. Why am I getting a single null element in a flow with non nullable elements?
Room is a db written in Java and that's why it ignores Kotlin optional. I suggest to declare always query return type or, in your case, Flow<T?>
type, optional. If you don't want a null type in the flow you can use filterNotNull()
function like this:
Tdao.getT(date).filterNotNull()
How Room handles nullability depends on how you define the return type of the query function. The Docs say:
- When the return type is
Flow<T>
, querying an empty table throws a null pointer exception.- When the return type is
Flow<T?>
, querying an empty table emits anull
value.- When the return type is
Flow<List<T>>
, querying an empty table emits an empty list.
The above snippet discusses empty tables, but I assume the same behaviour is applicable to any query that returns no rows.
Source: Room Docs on Query
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With