Sealed classes

Sealed classes represent restricted type hierarchies. They are similar to enums, but each class in the hierarchy can have its own properties and methods. 

In the following example, we've defined a sealed class named ViewState by using the sealed keyword:

sealed class ViewState
object Loading : ViewState()
data class Loaded(val article: Article) : ViewState()
class Error(val error: Throwable?) : ViewState()

We've created three classes that extend ViewStateThis class hierarchy may be defined within the class body or outside it. However, a sealed class may be subclassed only within the same file. 

Each of these classes has its own unique properties. You may also notice that we've used object, data, and regular classes when defining our sealed hierarchy:

sealed class ViewState
object Loading : ViewState()
data class Loaded(val article: Article) : ViewState()
class Error(val error: Throwable?) : ViewState()

By using sealed classes, we can indicate to the compiler all known types for the class. In this function, we are using a when statement to return a String based on the type of ViewState:

fun getLogMessage(state: ViewState) : String {
return when (state) {
Loading -> "is loading"
is Loaded -> "is loaded"
is Error -> "error has occurred"
}
}

Because we are using a when expression using a sealed class type, the compiler will enforce that every type is handled, or a default else branch is added. This is useful because it helps us to ensure all states are handled even if we add/remove types in the future.