Kotlin sealed class subclass needs to be casted to base class if provided as RxJava Observable

↘锁芯ラ 提交于 2020-11-28 04:47:08

问题


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.

java generic subtype




回答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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!