ProgrammingKotlin developer

What are sealed interfaces in Kotlin, how do they work in practice, and how do they differ from sealed classes?

Pass interviews with Hintsage AI assistant

Answer

Background

In earlier versions of Kotlin, sealed classes were used to restrict type hierarchies, allowing developers to explicitly control permissible subtypes by limiting the declaration of subclasses to a single file. For some tasks, this was insufficient, especially when it was necessary to describe similar hierarchies using interfaces. To solve this problem, starting from Kotlin 1.5, a sealed modifier for interfaces was introduced.

Problem

Regular interfaces allow implementation anywhere within the project, which often complicates ensuring absolute closure of type hierarchies and using exhaustive type checks (exhaustive when). This can lead to runtime errors and the inability to statically verify all variants.

Solution

Sealed interfaces allow limiting the list of implementing classes within a single file, enhancing the predictability of the type system and the safety of pattern matching.

sealed interface NetworkResult class Success(val data: String): NetworkResult class Failure(val error: Throwable): NetworkResult fun handle(result: NetworkResult) = when(result) { is Success -> println("Data: ${result.data}") is Failure -> println("Error: ${result.error}") // All variants are accounted for, 'else' is not needed }

Key features:

  • Sealed interfaces allow a single hierarchy: only classes/objects from the same file can inherit
  • Sealed interfaces can be implemented by both classes and objects
  • They expand type safety of the when expression without the need to explicitly provide else

Trick Questions.

Can a sealed interface be added in a separate file or implemented outside the original file?

No. All types directly implementing a sealed interface must be declared in the same file; otherwise, the compiler will throw an error.

Is it allowed for a sealed interface to have subinterfaces outside the declaration file?

No. Subinterfaces must also reside in the same file. The entire hierarchy is closed within the file.

Do sealed interfaces affect runtime performance?

Not directly, but they allow for safe type checks at compile time, reducing the likelihood of runtime errors, simplifying code, and potentially speeding up testing indirectly.

Common Errors and Anti-patterns

  • Declaring implementation classes of a sealed interface outside the file where the interface itself is declared
  • Adding too many variants, complicating maintenance
  • Using sealed interface when the hierarchy of variants may change (closure of variants is not required)

Real-life Example

Negative Case

A developer declares a sealed interface NetworkAction in one file, but implementations are scattered across different classes and files. The problem is identified late: the compiler signals a rule violation, and restructuring is complicated, especially in a large project.

Pros:

  • Allows declaring new implementations without editing a single file

Cons:

  • Violates guarantees of type safety
  • Requires code refactoring, which is time-consuming

Positive Case

The sealed interface OrderResult and the classes Success/Failure are declared within a single file. The when check is always exhaustive, with no branch missed.

Pros:

  • Safety and completeness of handling
  • Improves architectural quality

Cons:

  • Less flexible extensibility (all changes are only through this file)