In code, we often need to qualify some finite set of state values, such as:

  • Network request: successful — failed
  • Account status: VIP — Poor VIP — Ordinary
  • Toolbar: Expand — half fold — Shrink

And so on.

Typically, we use enum classes to encapsulate visible state values through enumerations.

enum class NetworkState(val value: Int) {
    SUCCESS(0),
    ERROR(1)
}
Copy the code

However, the disadvantages of enumerations are also obvious. First, enumerations take up more memory than normal code, and each enumeration can only define one instance and cannot extend much information.

There is also a way to seal a state using an abstract Class, but this has the obvious disadvantage of breaking the limitations of enumerations, so Kotlin proposes a new solution: Sealed Class.

Creating a state set

In this example, we will see how to use Sealed Class to seal a state.

Similar to an abstract Class, Sealed Class can be used to represent a hierarchy. It can subclass any class: data Class, plain Kotlin object, plain class, or even another Sealed class, so we define a Result Sealed class:

sealed class Result<out T : Any> {
    data class Success<out T : Any>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
}
Copy the code

Of course, it doesn’t have to be in the top-level class:

sealed class Result<out T : Any> 
data class Success<out T : Any>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
Copy the code

That’s fine. The difference is whether or not the reference contains a top-level class.

In most scenarios, the first method is recommended to clearly show the hierarchy of calls.

In this example, we define two scenarios, Success and Error, which represent the two hypothetical network states. In each state, Success, for example, can pass in a custom data type because it is itself a class, so by virtue of this, You can customize the scene values carried by the state. In the example above, we define that in Success, we pass data, and in Error, we pass Exception.

So the first step in using Sealed Class is to seal the scenario, comb through the specific scenario enumeration, and define the data types that need to be passed.

If the scene value does not require passing data, you can simply use: object XXXX and define a variable.

use

Let’s see how to use Sealed Class.

Val result = if (true) {result.success ("Success")} else {result.error (Exception(" Error "))}  when (result) { is Result.Success -> print(result.data) is Result.Error -> print(result.exception) } }Copy the code

In most cases, Sealed Class is used in conjunction with when. If the parameter is Sealed Class, you can quickly complete all the branches in the IDE without having to add the else branch, as Sealed Class is already complete.

So when and Sealed Class are a natural match.

Further simplification

We can also simplify the call to Sealed Class by extending the Sealed Class function. When will be used every time we use Sealed Class, and sometimes there will be some code redundancy.

inline fun Result<Any>.doSuccess(success: (Any) -> Unit) {
    if (this is Result.Success) {
        success(data)
    }
}

inline fun Result<Any>.doError(error: (Exception?) -> Unit) {
    if (this is Result.Error) {
        error(exception)
    }
}
Copy the code

Here I extend Result by adding two extensions doSuccess and doError, and accept two higher-order functions to receive processing behavior, making it easier to call.

result.doSuccess { }
result.doError { }
Copy the code

So when and Sealed Class and expansion functions are a natural fit.

Sealed Class is implemented using an abstract Class. The compiler generates a constructor that can only be called by the compiler, so that it does not need to be modified by other classes. Achieve a limit of Sealed Class.

Encapsulation?

Sealed Class is similar to an abstract Class in that it allows you to extend the logic.

sealed class TTS {
    
    abstract fun speak()

    class BaiduTTS(val value: String) : TTS() {
        override fun speak() = print(value)
    }

    class TencentTTS(val value: String) : TTS() {
        override fun speak() = print(value)
    }
}
Copy the code

This is handy if you want to extend, as shown in the code below.

class XunFeiTTS(val value: String) : TTS() {
    override fun speak() = print(value)
}
Copy the code

Sealed Class is an abstract Class that allows you to control the limitations of the state, so it is more flexible and convenient than enums.

Another example is the previous network encapsulation:

sealed class Result<out T : Any> {
    data class Success<out T : Any>(val data: T) : Result<T>()
    sealed class Error(val exception: Exception) : Result<Nothing>() {
        class RecoverableError(exception: Exception) : Error(exception)
        class NonRecoverableError(exception: Exception) : Error(exception)
    }

    object InProgress : Result<Nothing>()
}
Copy the code

Sealed Class allows you to easily extend the Error type, add new states, and more important, the IDE’s autocomplete function allows you to automatically generate conditional branches to avoid manual coding.

I would like to recommend my website xuyisheng. Top/focusing on Android-Kotlin-flutter welcome you to visit