Sealed Classes, Sealed Interfaces, and Enum Classes in Kotlin

Ömür Kumru
4 min readOct 12, 2022

--

Seal on the beach on dune island near helgoland
Seal on the beach on Dune island near Helgoland

One of the useful classes that we can use that entered our world with Kotlin is the sealed class/interface. At first glance, although it is very similar to the enum class we know from Java, there are differences in a few points.

What Sealed class mean?

Kotlin provides an important new type of class that is not present in Java. These are known as sealed classes. As the word sealed suggests, sealed classes conform to restricted or bounded class hierarchies. A sealed class defines a set of subclasses within it. It is used when it is known in advance that a type will conform to one of the subclass types. Sealed classes ensure type safety by restricting the types to be matched at compile-time rather than at runtime.

Sealed classes in Kotlin are implemented in the following manner.

sealed class A{
class B : A()
class C : A()
}

Why do we need Sealed classes over Enum classes?

Have to clarify one thing before going further. The sealed class/interface is not a replacement for the Enum class. Sealed class/interface is another tool in our toolbox. It is still our responsibility to use the right tool at the right time.

Until some point, they are capable of doing the same job(ignoring memory allocation for now) Check their similarities first.

What are their similarities?

Start with the similarities between Sealed Class, Sealed Interface, and Enum Class. Let’s say we have network call errors that we need to handle.

sealed class HttpErrorSealed {
object Unauthorized : HttpErrorSealed()
object NotFound : HttpErrorSealed()

fun doSomething() {}
}

sealed interface HttpErrorInterface {
object Unauthorized : HttpErrorInterface
object NotFound : HttpErrorInterface

fun doSomething() {}
}

enum class HttpErrorEnum {
Unauthorized,
NotFound;

fun doSomething() {}
}

As you can see, we can do the same with these three approaches. We can even define functions in them.

Also, the enum can actually be considered even more useful as we can call valueOf, values functions and return the elements in it while not a case for Sealed Class/Interface.

//this is possible with enum only
HttpErrorEnum.values().forEach {...}

However, the similarities end after this point.

What are their differences?

Let's expand our example above a bit. As you know, network responses have codes. It's time to add this to our example.

sealed class HttpError(val code: Int) {
object Unauthorized : HttpError(401)
object NotFound : HttpError(404)
}

enum class HttpErrorEnum(val code: Int) {
Unauthorized(401),
NotFound(404)
}
//cannot pass variable with interface
sealed interface HttpErrorInterface {
object Unauthorized : HttpErrorInterface
object NotFound : HttpErrorInterface
}

As you can notice, the code variable can be defined in the constructor for sealed class and enum, but this cannot be done in the sealed interface. This is because interfaces do not have constructors. In this way, we have seen the limitation of the sealed interface.

While we've seen similar structures so far, it's time to show the real potential of a sealed class/interface.

Object and data class in sealed class/interface

As you may have noticed, we have only used object in the sealed class/interface so far. But we can also use data classes.

Continuing from the above example, when we get any network error this may include the reason in some cases.

sealed class HttpError(val code: Int) {
data class Unauthorized(val reason: String) : HttpError(401)
object NotFound : HttpError(404)
}

sealed interface HttpErrorInterface {
data class Unauthorized(val reason: String) : HttpErrorInterface
object NotFound : HttpErrorInterface
}

Here we can handle it differently for each response by using data class instead of object and we don’t need to define it in advance.

Using Sealed class, Sealed interface, and Enum

We use Sealed class, Sealed interface, and Enum to handle possible cases. Let's give a code sample of the use of the classes we mentioned above.

val sealedClassError: HttpError = HttpError.Unauthorized("token not valid")
when (sealedClassError) {
HttpError.NotFound -> Unit
is HttpError.Unauthorized -> Unit
}

val sealedInterfaceError: HttpErrorInterface = HttpErrorInterface.Unauthorized("token not valid")
when (sealedInterfaceError) {
HttpErrorInterface.NotFound -> Unit
is HttpErrorInterface.Unauthorized -> Unit
}

val enumError: HttpErrorEnum = HttpErrorEnum.Unauthorized
when (enumError) {
HttpErrorEnum.Unauthorized -> Unit
HttpErrorEnum.NotFound -> Unit
}

As you can see, we can easily control every case by when without the need to add else.

So why do we need Sealed interface at all?

The difference between enum and sealed seems relatively easy. However, this is not the case for the sealed interface.

There are two reasons why we chose the Sealed Interface over Sealed Class.

Less code

If you don’t need the constructor, you can get rid of it. In this way, you can get rid of writing a little unnecessary code.

Reduce Memory Allocation

This is our main reason. If we are able to use Interface instead of Class, then we can reduce the amount of memory allocated in this way. (Approx twice less for the interface in comparison to the class)

In this article, I hope a few things have become clear to you, even if it is small. See you in the next articles. Happy coding days.

References

https://www.geeksforgeeks.org/kotlin-sealed-classes/

https://www.digitalocean.com/community/tutorials/kotlin-sealed-class

https://www.youtube.com/c/PhilippLackner

--

--

Ömür Kumru

Living in the Netherlands. Android Developer @ ABN AMRO, enthusiastic about software development, always wants to follow best practices.