Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make sealed classes generic in kotlin?

Tags:

kotlin

Is it possible to use the AsyncResult class below to prevent redefining InFlight, Error and InFlight in UserDataAppResult and CreateUserResult?

//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()
}

Is it possible for the above code to look like this?

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)
}

Currently UserDataAppResult.Error is not found which makes sense. But is it possible to reuse the AppResult sealed class hierarchy and introduce new types.

like image 350
Rory Kelly Avatar asked May 29 '17 13:05

Rory Kelly


People also ask

Can we inherit sealed class in Kotlin?

One of the advantages of sealed interfaces over sealed classes is the ability to inherit from multiple sealed interfaces. This is impossible for sealed classes because of the lack of multiple inheritance in Kotlin.

How do I call a sealed class at Kotlin?

To define a sealed class, just precede the class modifier with the sealed keyword. The sealed classes also have one another distinct feature, their constructors are protected by default. A sealed class is implicitly abstract and hence it cannot be instantiated.

Can we create object of sealed class Kotlin?

Rules of a Sealed class in Kotlin We cannot create the object of a sealed class directly and can have abstract members. By default the constructor of a sealed class is private and we cannot make it as non-private.

Can you have sealed interface in Kotlin?

Sealed interfaces were just introduced in Kotlin 1.5 and are a very powerful new feature.


2 Answers

your Object can't have a generic type in Kotlin but this could be solved simply by following the example below:

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>()
}

writing:

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))
}

reading:

state.observe(..., {state -> 
  when(state) {
    Loading -> showLoading()
    is Error -> showError(state.throwable)
    is Success -> onSuccess(state.item)
  }
}
like image 61
Kosh Avatar answered Oct 07 '22 06:10

Kosh


It's not possible in Kotlin. Every type you use must have an explicitly declared class somewhere. Classes are not created implicitly by the compiler even in the case when nested classes are declared in the superclass.

For your problem, I recommend you rewrite the code from combining two inheritance-based hierarchies to one of the two combining inheritance and composition, or just restructure the hierarchy in some way, for example (I suppose the exact instance of a result would be irrelevant to you in case when it's not 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)
    }
}
like image 44
Alexander Udalov Avatar answered Oct 07 '22 06:10

Alexander Udalov