I am trying to use sealed class inheritance to organize data in a recyclerview which consumes data through PagingData, and I have been able to make it work but there is situation that I could not understand how to resolve other than to cast the subclasses to the parent class.
I have the following code example:
sealed class SealedClass{
data class SubClassData(val data: String) : SealedClass()
data object SubClass : SealedClass()
}
fun test() {
val n = flowOf(PagingData.from(List(4){SealedClass.SubClass}))
val j = flowOf(PagingData.from(List(4){SealedClass.SubClassData("data")}))
val k: Flow<PagingData<SealedClass>> = merge(n, j) <--- error shows here
}
This results in the following error for val k
when trying to merge the two:
This is strange to me because if I use them as plain lists, it never complains about such an issue.
To resolve the above error I am forced to do the following:
sealed class SealedClass{
data class SubClassData(val data: String) : SealedClass()
data object SubClass : SealedClass()
}
fun test() {
val n: Flow<PagingData<SealedClass>> = flowOf(PagingData.from(List(4){SealedClass.SubClass}))
val j: Flow<PagingData<SealedClass>> = flowOf(PagingData.from(List(4){SealedClass.SubClassData("data")}))
val k: Flow<PagingData<SealedClass>> = merge(n, j) <--- no error
}
Now there is no more error and it works perfectly.
Why is this happening I am trying to understand, but couldn't find anything related to it since I don't know what to search for other than generic terms like "out" and "projection", and because of this I also don't know if there is a way to resolve this other than cast the flows (n and j) to the parent class, was hoping that maybe the way I am structuring the sealed class was wrong, but all examples I found showed the same as mine.
Is there anyway to avoid this issue? And if possible, can anyone explain why this happens in the case above, but it is perfectly fine if I was using instead like this:
fun test() {
val n = List(4){SealedClass.SubClass}
val j = List(4){SealedClass.SubClassData("data")}
val k: List<SealedClass> = n + j <--- no error even though no cast was needed
}
Thank you in advance for all the help.
The default variance of a PagingData's type is invariant. That means that a PagingData<SubClass>
does not qualify as a subtype of PagingData<SealedClass>
.
You described this as working with Lists. This is because List
is defined with declaration site covariance. (The List interface is declared as interface List<out E>
.) Therefore its default variance is covariant. That means List<SubClass>
does qualify as a subtype of List<SealedClass>
.
If you tried to do this with MutableLists, which do not have declaration site covariance, it wouldn't work either.
To merge the two flows, you need a common supertype. So you need to use covariance to make this possible without stripping away the type information. Although PagingData is not defined with declaration site covariance, you can utilize use site covariance:
val k: Flow<PagingData<out SealedClass>> = merge(n, j)