如何在kotlin中制作密封类通用?

Ror*_*lly 10 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()
}
Run Code Online (Sandbox Code Playgroud)

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

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)
}
Run Code Online (Sandbox Code Playgroud)

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

k0s*_*0sh 34

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>()
}
Run Code Online (Sandbox Code Playgroud)

写作:

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))
}
Run Code Online (Sandbox Code Playgroud)

读:

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

  • 更新:想通了 (2认同)

Ale*_*lov 6

在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)
    }
}
Run Code Online (Sandbox Code Playgroud)


Mah*_*raa 5

受到@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>()
}
Run Code Online (Sandbox Code Playgroud)

-> 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)
}
Run Code Online (Sandbox Code Playgroud)

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

-> 查看:

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

-> 在布局上我们可能需要更多的调整

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
}
Run Code Online (Sandbox Code Playgroud)

toData 仅在成功时提供数据

    <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}' />
Run Code Online (Sandbox Code Playgroud)

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

祝你好运,'。