Fixed
Status Update
Comments
da...@google.com <da...@google.com> #2
Definitely want to support this. Currently investigating moving the Callbacks to interfaces so that wrapping/conversion can be done:
private abstract class WrapperDataSource<in A, B>(private val source: PositionalDataSource<A>)
: PositionalDataSource<B>() {
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<B>) {
source.loadInitial(params, object: LoadInitialCallback<A> {
override fun onResult(data: List<A>, position: Int, totalCount: Int) {
callback.onResult(convert(data), position, totalCount)
}
override fun onResult(data: List<A>, position: Int) {
callback.onResult(convert(data), position)
}
})
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<B>) {
source.loadRange(params) { data -> callback.onResult(convert(data)) }
}
protected abstract fun convert(source: List<A>): List<B>
}
Making them interfaces makes it easy to wrap/test. Downside is that external callers can't use some of the guts of the callback impl (like validating non-negative positions), but this should make it much more reasonable to wrap, decorate, or otherwise post-process data loaded from e.g. Room. In the network case, the equivalent might be post-processing a network request in a way that's awkward to inject into network loading code.
private abstract class WrapperDataSource<in A, B>(private val source: PositionalDataSource<A>)
: PositionalDataSource<B>() {
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<B>) {
source.loadInitial(params, object: LoadInitialCallback<A> {
override fun onResult(data: List<A>, position: Int, totalCount: Int) {
callback.onResult(convert(data), position, totalCount)
}
override fun onResult(data: List<A>, position: Int) {
callback.onResult(convert(data), position)
}
})
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<B>) {
source.loadRange(params) { data -> callback.onResult(convert(data)) }
}
protected abstract fun convert(source: List<A>): List<B>
}
Making them interfaces makes it easy to wrap/test. Downside is that external callers can't use some of the guts of the callback impl (like validating non-negative positions), but this should make it much more reasonable to wrap, decorate, or otherwise post-process data loaded from e.g. Room. In the network case, the equivalent might be post-processing a network request in a way that's awkward to inject into network loading code.
Description
room-common (androidX)
room-coroutines (androidX)
Kotlin
Version used: 2.1.0-alpha03
Devices/Android versions reproduced on: Compilation error
This issue happens when you try to make a concret Room Dao inherit from a generic Dao using coroutine (with suspend keyword on them), it displays the following error :
error: Type of the parameter must be a class annotated with @Entity or a collection/array of it.
kotlin.coroutines.Continuation<? super kotlin.Unit> p1);
^
Classes I use to reproduce this problem :
@Database(
version = 1,
entities = [
User::class
],
exportSchema = true
)
abstract class RoomDB : RoomDatabase() {
abstract fun getUserDao(): UserDao
}
@Entity(tableName = "user")
data class User(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val firstName: String,
val lastName: String
)
interface BaseDao<T> {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertOrUpdate(entity: T)
}
@Dao
interface UserDao: BaseDao<User> {
@Query("SELECT * FROM user")
suspend fun loadAll(): List<User>
}
I tried several things, like using varags in parameter on insertOrUpdate method, or giving it an output (List<Int> or Int) without any change in the error.
A sample project is in attachment to reproduce this bug.
Thank you in advance