繁体   English   中英

如何在 kotlin 中使密封类通用?

[英]How to make sealed classes generic in kotlin?

是否可以使用下面的 AsyncResult 类来防止在 UserDataAppResult 和 CreateUserResult 中重新定义 InFlight、Error 和 InFlight?

//TODO: use this to make the below classes generic?
sealed class AsyncResult{
  object InFlight : AsyncResult()
  data class Error(val errorMessage: String) : AsyncResult()
  data class Loaded<out T>(val users: T) : AsyncResult()
}

sealed class UserDataAppResult : AppResult() {
  object InFlight : UserDataAppResult()
  data class Error(val errorMessage: String) : UserDataAppResult()
  data class Loaded(val users: List<User>) : UserDataAppResult()
}

sealed class CreateUserResult : AppResult() {
  object InFlight : CreateUserResult()
  data class Error(val errorMessage: String) : CreateUserResult()
  data class Loaded(val users: User) : CreateUserResult()
}

上面的代码有可能看起来像这样吗?

sealed class AsyncResult{
  class InFlight : AsyncResult()
  data class Error(val errorMessage: String) : AsyncResult()
  data class Loaded<out T>(val users: T) : AsyncResult()
}

sealed class UserDataAppResult : AsyncResult()
sealed class CreateUserResult : AppResult()

val activeUsers: Flowable<UserDataAppResult> = appDatabase.userDao().getActiveUsers(appSettings.currentLanguage.ordinal)
    .map<UserDataAppResult> { UserDataAppResult.Loaded(it) }
    .onErrorReturn { UserDataAppResult.Error(it.localizedMessage) }
    .startWith(UserDataAppResult.InFlight)
    .observeOn(AndroidSchedulers.mainThread())
    .share()

fun createUser(): Flowable<CreateUserResult> {

  val userId = UUID.randomUUID().toString()
  val user = User()
  user.id = userId
  return appDatabase.userDao().insertAll(user)
      .map <CreateUserResult> { CreateUserResult.Loaded(user) }
      .onErrorReturn { CreateUserResult.Error(it.localizedMessage) }
      .startWith(CreateUserResult.InFlight)
}

目前未找到有意义的 UserDataAppResult.Error。 但是是否可以重用 AppResult 密封类层次结构并引入新类型。

您的Object在 Kotlin 中不能有泛型类型,但这可以通过以下示例简单地解决:

sealed class ResponseState<out T> {
    object Loading : ResponseState<Nothing>()
    data class Error(val throwable: Throwable) : ResponseState<Nothing>()
    data class Success<T>(val item: T) : ResponseState<T>()
}

写作:

val _state = MutableLiveData<ResponseState<MessageModle>>() 


_state.postValue(ResponseState.Loading)

myNetworkCall { response, e
  if (e != null) _state.postValue(ResponseState.Error(e))
  else _state.postValue(ResponseState.Success(response))
}

阅读:

state.observe(..., {state -> 
  when(state) {
    Loading -> showLoading()
    is Error -> showError(state.throwable)
    is Success -> onSuccess(state.item)
  }
}

这在 Kotlin 中是不可能的。 您使用的每种类型都必须在某处具有显式声明的类。 即使在超类中声明嵌套类的情况下,编译器也不会隐式创建类。

对于您的问题,我建议您将代码从组合两个基于继承的层次结构重写为两个组合继承和组合之一,或者只是以某种方式重构层次结构,例如(我想结果的确切实例是无关紧要的给你,以防它没有Loaded ):

sealed class AsyncResult {
    object InFlight : AsyncResult()
    data class Error(val errorMessage: String) : AsyncResult()
    sealed class Loaded<out T>(val result: T) : AsyncResult() {
        sealed class UserDataAppResult(users: List<User>) : Loaded<List<User>>(users)
        sealed class CreateUserResult(user: User) : Loaded<User>(user)
    }
}

通过 Google 指南: https ://developer.android.com/jetpack/docs/guide

sealed class Resource<T>(
        val data: T? = null,
        val message: String? = null
) {
    class Success<T>(data: T) : Resource<T>(data)
    class Loading<T>(data: T? = null, var refreshing: Boolean = false) : Resource<T>(data)
    class Error<T>(data: T? = null, message: String) : Resource<T>(data, message)
}

受到@kosh 解决方案的启发

-> 视图状态:

sealed class ViewState<out T> {
    object Loading : ViewState<Nothing>()
    data class Error(val throwable: Throwable) : ViewState<Nothing>()
    data class Success<T>(val item: T) : ViewState<T>()
}

-> 在 ViewModel 中:

private val _homeVS = MutableLiveData<ViewState<HomeMode>>()
val homeVS: LiveData<ViewState<HomeMode>> get() = _homeVS

// start requesting API 
_homeVS.value = ViewState.Loading
try {
    val result = loadData()
    _homeVS.value = ViewState.Success(result)
} catch (e: Exception) {
    _homeVS.value = ViewState.Error(e)
}

然后你可以在布局/视图中使用这个泛型

-> 视图:

viewModel.homeVS.observe(viewLifecycleOwner, {state ->
        when(state) {
            is ViewState.Error -> showError(state.throwable)
            is ViewState.Success -> onSuccess(state.item)
            ViewState.Loading -> showLoading()
        }
    })

-> 在布局上,我们可能需要一点晨间调整

sealed class ViewState<out T> {
    object Loading : ViewState<Nothing>()
    data class Error(val throwable: Throwable) : ViewState<Nothing>()
    data class Success<T>(val item: T) : ViewState<T>()

    fun toData(): T? = (this as? Success)?.item
}

toData 仅在 onSuccess 上提供数据

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text='@{vm.homeVS.toData() != null ? vm.homeVS.toData().param1 : ""}' />

    <!--onLoading-->
    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility='@{vm.homeVS instanceof ViewState.Loading ? View.VISIBLE : View.GONE}' />

    <!--onError-->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility='@{vm.homeVS instanceof ViewState.Error ? View.VISIBLE : View.GONE}' />

当然,使用 BindingAdapter,您可以让它变得更好。 这里只是为了说明解决方案。

祝你好运,'。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM