Sealed Class vs Sealed Interface in Kotlin

Manuchekhr Tursunov
4 min readApr 28, 2023

Sealed class and Sealed interface are two features in Kotlin that allow the creation of restricted class hierarchies. Both of these constructs are used to define a finite set of possible subtypes, and prevent additional subtypes from being defined outside the declared hierarchy.

Sealed Class:

A sealed class is a class that can be subclassed, but only inside the same file where it is declared. This means that a sealed class cannot be directly instantiated and cannot have any other subclasses outside the file it is declared in. It is commonly used to represent a restricted hierarchy of classes.

Here is an example of a sealed class:

sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val error: Exception) : Result<Nothing>()
}

In this example, we have a sealed class named Result that has two subclasses, Success and Error. The Success subclass takes a generic type T as a parameter, which represents the data that is returned on success. The Error subclass takes an Exception as a parameter, which represents the error that occurred.

  • Sealed classes can have constructors with parameters, while sealed interfaces cannot.
  • Sealed classes can have abstract methods and properties, while sealed interfaces can only have abstract methods.
  • Sealed classes can be extended by classes, objects, and other sealed classes, while sealed interfaces can only be implemented by classes and objects.
  • Sealed classes are often used in conjunction with when expressions to provide exhaustive pattern matching.

Example:

sealed class Animal
class Dog(val name: String): Animal()
class Cat(val name: String): Animal()

fun makeSound(animal: Animal) = when (animal) {
is Dog -> println("${animal.name} says woof!")
is Cat -> println("${animal.name} says meow!")
}

val myDog = Dog("Rufus")
val myCat = Cat("Whiskers")
makeSound(myDog) // outputs: "Rufus says woof!"
makeSound(myCat) // outputs: "Whiskers says meow!"

Sealed Interface:

A sealed interface is similar to a sealed class, but it is used to represent a restricted set of interfaces rather than classes. Like sealed classes, sealed interfaces restrict the set of possible subtypes to a finite set that is defined within the same file as the sealed interface.

Here is an example of a sealed interface:

sealed interface State {
object Idle : State
data class Loading(val message: String) : State
data class Error(val error: Throwable) : State
data class Success(val data: Any) : State
}

In this example, we have a sealed interface named State that has four subtypes: Idle, Loading, Error, and Success. Each subtype represents a different state that an application might be in, such as when the application is idle, loading data, encountering an error, or successfully retrieving data.

  • Sealed interfaces cannot have constructors with parameters, but they can have properties.
  • Sealed interfaces can only have abstract methods, but they can also have default implementations for those methods.
  • Sealed interfaces can be implemented by classes and objects, but not extended by other interfaces or sealed interfaces.
  • Sealed interfaces are often used as a way to define a set of related functionality that can be implemented by different classes.

Example:

sealed interface Animal {
val name: String
fun makeSound()
}

class Dog(override val name: String): Animal {
override fun makeSound() {
println("$name says woof!")
}
}

class Cat(override val name: String): Animal {
override fun makeSound() {
println("$name says meow!")
}
}

val myDog = Dog("Rufus")
val myCat = Cat("Whiskers")
myDog.makeSound() // outputs: "Rufus says woof!"
myCat.makeSound() // outputs: "Whiskers says meow!"

Sealed classes are used to define a restricted hierarchy of classes, while Sealed interfaces are used to define a set of related functionality that can be implemented by different classes. Sealed classes can have constructors and abstract properties, while Sealed interfaces cannot have constructors but can have properties and default method implementations.

When to Use Sealed Classes vs Sealed Interfaces:

Sealed classes and sealed interfaces can both be used to define restricted class hierarchies, but they are used in different contexts. Sealed classes are typically used to represent a restricted hierarchy of classes, whereas sealed interfaces are used to represent a restricted set of interfaces.

In general, if you need to restrict the set of possible subclasses for a class hierarchy, you should use a sealed class. If you need to restrict the set of possible implementations for an interface, you should use a sealed interface.

In summary, sealed classes and sealed interfaces are powerful features in Kotlin that allow developers to define restricted class hierarchies and prevent additional subtypes from being defined outside the declared hierarchy. They are useful in situations where a finite set of possible subtypes is needed, and can help ensure that code is more reliable and less error-prone.

--

--