问题
I'm trying to avoid terminal states of RxJava chains in my app written in Kotlin, so I found out that it is right thing to transform Observable<T>
to Observable<Result<T>>
where Result
is sealed class.
sealed class Result<T>
data class Success<T>(val data: T) : Result<T>()
data class Failure<T>(val throwable: Throwable) : Result<T>()
And let's say I have this network request observable.
fun getOrganization(): Observable<Result<Boolean>> {
return api.getOrganization("google")
.map { Success(true) }
.onErrorReturn { Failure(RuntimeException("throwable")) }
}
So what I want to do in the end is to handle result like this.
fun main(args: Array<String>) {
getOrganization().subscribe({
when (it) {
is Success -> print("success = ${it.data}")
is Failure -> print("failure = ${it.throwable}")
}
})
}
Everything should work great, but I have this error in IDE.
Isn't it just simple using of subclass instead of base class?
I found out that if I explicitly cast Success(true)
to Result<Boolean>
everything works, but shows this warning.
Why it even happens and what am I doing wrong here?
UPD.
It looks like the main issue here is near rxjava's onErrorReturn operator. If I remove it completely, then it works even without out
keyword like answers suggest.
If I return Success
from both map and onErrorReturn then out
keyword helps to get rid of errors.
But if I return Failure
from onErrorReturn there's still error. Compiler has to know that Failure
Result
typed with the same Boolean
as Success
. What should I do to avoid it or to satisfy it's requirements?
回答1:
maybe you misunderstand the generic variance in kotlin. it works fine since Success<Boolean>
is a subtype of Result<Boolean>
. so the "No need cast" warning reported and the code below works fine:
val ok:Success<Boolean> = Success(true);
val result:Result<Boolean> = ok;
But you can't assign a Success<Boolean>
to a Result<Any>
since their type parameters are different, so this why compiler reports the "Type mismatch" error , for example:
val ok:Success<Boolean> = Success(true);
val result1:Result<Any> = ok;// error
val result2:Result<out Any> = ok;// ok
to fix the error and warnings you can try the code below:
fun getOrganization(): Observable<out Result<Boolean>> {
return api.getOrganization("google")
.map<Result<Boolean>> { Success(true) }
.onErrorReturn { Failure(RuntimeException("throwable")) }
}
for more details, you can see java generic subtypes & kotlin type projections.
回答2:
An Observable<Success<Boolean>>
is not a subtype Observable<Result<Boolean>>
, just as List<String>
is not a subtype of List<Object>
. See the Generics documentation.
To solve this, either return an Observable<out Result<Boolean>>
, or explicitly add a type to your map
function:
.map<Result<Boolean>> { Success(true) }
来源:https://stackoverflow.com/questions/44564836/kotlin-sealed-class-subclass-needs-to-be-casted-to-base-class-if-provided-as-rxj