TL;DR version
I'd like to have a method whose returning signature is LiveData<User>, in which User is an interface. And the implementation returns a LiveData<UserEntity> in which UserEntity is a concrete class that extends User. Like following:
fun getUser(id: String): LiveData<User> {
// for brevity
return LiveData<UserEntity>() // actual code will retrieve data from db
}
The compiler complains return type not match error
TL; version
I'm reading the Jetpack guide. In it, they designed a DataRepository class which can retrive data from either DB or WebService, as shown in this graph:
And they claim that
Notice that each component depends only on the component one level below it.
It's an appealing design, I actually tried to replicate it in my own project. However, I found WebService inevitably depends on Room, because it's method getUser() returns a LiveData of User, which is a Room Entity.
This issue might not break the app, but it's contradicting the fundamental design of the"separation of concerns" principle, and is aesthetically ugly IMHO.
My question is: are there any workaround? I've tried to refactor User as an interface and implemented an UserEntity class that extends it. Shown below:
interface User {
val id: String
val name: String
val lastName: String
}
@Entity(tableName="users")
data class UserEntity(
override val id: String
override val name: String
override val lastName: String) : User
@Dao
interface UserDao() {
@Query("SELECT * FROM users where id = :id")
fun getUser(id: String): LiveData<UserEntity> // UserEntity instead of User, because Room doesn't know how to construct an interface
}
Note I have to return UserEntity intead of User, because Room doesn't know how to construct an interface.
Here is the problem: the following code won't compile:
interface DataRepository {
fun getUser(id: String): LiveData<User>
}
class DbRepository : DataRepository {
// ... init db connection/userDao ...
override fun getUser(id: String): LiveData<User> = userDao.getUser(id)
}
The error indicates
data type mismatch: LiveData<User> vs. LiveData<UserEntity>
The above code won't work, as you can't expect a function to return multiple data types. I recommend using one User class which merges both Room and GSON mapping.
@Entity(tableName="users")
data class Users(
@PrimaryKey // Room annotation
@SerializedName("id") // Gson annotation
val id: String,
@ColumnInfo(name = "name")
@SerializedName("name")
val name: String,
@ColumnInfo(name = "lastName")
@SerializedName("lastName")
val lastName: String,
)
You can get rid of your UserEntity class and use the above as the DTO for both API and DB repository.
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