簡體   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