Kotlin 类型不匹配编译错误:Require Success<T>, Found MyError

jay*_*917 3 android kotlin rx-java

我遇到一个问题,以下代码无法在 kotlin 中编译。

// StateModel.kt
sealed class StateModel
class Loading : StateModel()
data class Success<T: Any>(val data: T) : StateModel()
data class MyError(val message: String) : StateModel()

// StateModelTransformer.kt
class StateModelTransformer<T: Any> : FlowableTransformer<T, StateModel> {

  override fun apply(upstream: Flowable<T>): Publisher<StateModel> {
    return upstream
      .map { data -> Success(data) }
      .onErrorReturn { error ->
        MyError(error.message)  // compile error, Type mismatch, Require Success<T>, Found MyError
      }
      .startWith(Loading()) // compile error, none of the following function can be called with the arguments supplied
  }

}
Run Code Online (Sandbox Code Playgroud)

我不知道为什么onErrorReturn说需要Success<T>类型而不是StateModel类型。

谢谢

eph*_*ent 6

以下是相关声明Flowable,供参考。让我们忽略onErrorReturn;这与这里的问题无关。

public Flowable<T> {
    public <R> Flowable<R> map(Function<? super T, ? extends R> mapper);
    public Flowable<T> startWith(T value);
}
Run Code Online (Sandbox Code Playgroud)

这些是 Kotlin 推断的类型。

val upstream: Flowable<T>
val mapper:   (T) -> Success<T>    = { data -> Success(data) }
val map:      ((T) -> Success<T>) -> Flowable<Success<T>>
                                   = upstream::map
val mapped:   Flowable<Success<T>> = map(mapper)
val loading:  Loading              = Loading()
val startWith:
    (Success<T>) -> Flowable<Success<T>>
                                   = mapped::startWith

startWith(loading)  // type mismatch
Run Code Online (Sandbox Code Playgroud)

更具体的Success<T>类型已经在前面推断出来了,Kotlin 不会回溯来寻找更通用的StateModel类型。要强制发生这种情况,您可以手动声明类型,例如

// be explicit about the general type of the mapper
upstream.map { data -> Success(data) as StateModel }.startWith(Loading())

// be explicit about the generic type R = StateModel
upstream.map<StateModel> { data -> Success(data) }.startWith(Loading())
Run Code Online (Sandbox Code Playgroud)

顺便说一句,你目前正在<T>失败StateModel。我建议更改基类以包含类型参数。

sealed class StateModel<out T: Any>
object Loading : StateModel<Nothing>()
data class Success<T: Any>(val data: T) : StateModel<T>()
data class MyError(val message: String) : StateModel<Nothing>()
Run Code Online (Sandbox Code Playgroud)

例如,这可以让你写:

val <T: Any> StateModel<T>.data: T?
    get() = when (this) {
        is Success -> data
        else -> null
    }
Run Code Online (Sandbox Code Playgroud)