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?
Implementing Kotlin Sealed Classes To specify a sealed class, you need to add the modifier sealed . A sealed class cannot be instantiated.
Sealed classes are used for representing restricted class hierarchies, when a value can have one of the types from a limited set, but cannot have any other type.
note. enum classes can't extend a sealed class (as well as any other class), but they can implement sealed interfaces.
Sealed classes give us the flexibility of having different types of subclasses and also containing the state.
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.
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) }
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