Sealed classes in Kotlin: enums with super-powers (KAD 28)

Sealed classes in Kotlin are another new concept we didn’t have in Java, and open another new world of possibilities.

A sealed class allows you to represent constrained hierarchies in which an object can only be of one of the given types.

That is, we have a class with a specific number of subclasses. What we get in the end is a concept very similar to an enum. The difference is that in the enum we only have one object per type, while in the sealed classes we can have several objects of the same class.

This difference will allow objects from a sealed class to keep state. This will bring us some advantages that we’ll see in a moment, and also opens the doors to some functional ideas.

How to use sealed classes

Implementing a sealed class is actually very simple. Let’s use as an example a set of operations that can be applied to integers.

The implementation would be as follows:

sealed class Operation {
    class Add(val value: Int) : Operation()
    class Substract(val value: Int) : Operation()
    class Multiply(val value: Int) : Operation()
    class Divide(val value: Int) : Operation()

We create a sealed class called Operation, which contains four types of operations: addition, subtraction, multiplication and division.

The good thing about this is that now when expressions will require us to provide branches for all possible types:

fun execute(x: Int, op: Operation) = when (op) {
    is Operation.Add -> x + op.value
    is Operation.Substract -> x - op.value
    is Operation.Multiply -> x * op.value
    is Operation.Divide -> x / op.value

If you leave any of the subclasses out, when will complain and it won’t compile. If you implement them all, you don’t need else statement. And in general it won’t be recommended because that way we’re sure that we’re doing the right thing for all of them.

This is also great in case you decide to add a new operation, because it’ll fail at compile time and won’t run. Add a couple more operations: increment and decrement:

sealed class Operation {
    object Increment : Operation()
    object Decrement : Operation()

You’ll see that the compiler now warns you that there is a problem. Just add branches for these new operations:

fun execute(x: Int, op: Operation) = when (op) {
    Operation.Increment -> x + 1
    Operation.Decrement -> x - 1

You may have noticed I did something different. I used objects instead of classes. This is because if a subclass doesn’t keep state, it can just be an object. All the instances you create for that class would be exactly the same, as they can’t have different state.

Then, in the when expression you can get rid of is for those cases. Here you can just compare the object, as there’s only one instance, you don’t need to check the type of object. It would work too if you keep is for those too.

If you think about it carefully, a sealed class where all subclasses are objects would be the same as an enum.

Moving side effects to a single point

Side effects are a very recurring concept in functional programming. Functional programming relies heavily on the idea that for a given function, same parameters will return the same result.

Any state that is modified may break this assumption. But any program needs to modify states, communicate with input/output elements, etc. So it’s important to spot these operations very specific places in our code that can be easily isolated.

For example, any operations performed on an Android view can be considered a side effect, as the status of the views is being modified and the functions aren’t aware of it.

We could create a sealed class that would allow us to do operations on our views. Based on the idea of our previous example:

sealed class UiOp {
    object Show: UiOp()
    object Hide: UiOp()
    class TranslateX(val px: Float): UiOp()
    class TranslateY(val px: Float): UiOp()

fun execute(view: View, op: UiOp) = when (op) {
    UiOp.Show -> view.visibility = View.VISIBLE
    UiOp.Hide -> view.visibility = View.GONE
    is UiOp.TranslateX -> view.translationX = op.px
    is UiOp.TranslateY -> view.translationY = op.px

Remember: operations that have no state can be objects, because we don’t need different instances.

Now you can create a Ui object that accumulates all interface operations that we want to do over a view, but it won’t execute them until the moment we want.

We’ll have a description of what we want to do, and then we can create a component that executes them:

class Ui(val uiOps: List = emptyList()) {
    operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)

The Ui class stores a list of operations, and specifies a sum operator that will help make everything a bit cleaner and easier to read. Now we can specify the list of operations that we want to perform:

val ui = Ui() +
        UiOp.Show +
        UiOp.TranslateX(20f) +
        UiOp.TranslateY(40f) +

run(view, ui)

And then run it. Here I’m just using a run function, but this could be a complete class if required.

fun run(view: View, ui: Ui) {
    ui.uiOps.forEach { execute(view, it) }

Imagine the power that gives you this. Right now all you do is run the operations sequentially, but this could be as complex as required.

This run function could be passed to another function or a class, and the way those operations are run would be totally interchangeable. Remember you can pass functions as arguments.


The concept of the sealed classes is very simple, but it’s the basis of a lot of new ideas you need to get used if you haven’t played with functional programming before.

I must say that I’m not yet able to take the most out of sealed classed due to my knowledge limitations in functional programming.

If all this passionate you as to me, I encourage you to sign up for my free training where I will tell you everything you need to learn about how to create your Android Apps in Kotlin from scratch.

12 thoughts on “Sealed classes in Kotlin: enums with super-powers (KAD 28)”

  1. The examples you provide are always the best part to understand the concept. This was the third article about sealed class that I read and was the first one that I really understood, totally because the examples.

    Thank you 🙂

  2. The article was really easy to follow though. I have a question about the when expression in the execute function. Will it be wrong if I check whether op is of type UiOp.Show using **is UiOp.Show** even though it has no state?

    1. Yeah, not the end of the world. It’s probably more efficient to check if it’s the same object rather than checking if it’s an instance of the same class, but don’t think it’s a deal breaker at all.

  3. Very well explained Antonio, good job with examples, very simple. Now need to implement this. Thanks for the post.

Comments are closed.