ProgrammingBackend Developer

What is a sealed interface in Kotlin, how does it work, and what is it used for? Describe the features of usage, limitations, and provide an example.

Pass interviews with Hintsage AI assistant

Answer.

History of the question:

Interfaces with the sealed modifier appeared in Kotlin as an evolution of the sealed class concept. Prior to Kotlin 1.5, only sealed classes allowed restricting the set of possible subclasses, which is especially important for safely working with state hierarchies (state machines, DSL, etc.). By introducing sealed interfaces, developers provided a way to similarly restrict implementations of interfaces without tying them to classes.

Problem:

A simple open interface can be implemented anywhere in the program, which may lead to uncontrolled growth in the number of implementations and complicate code maintenance. When processed through a when expression, the compiler cannot warn about unaccounted branches.

Solution:

A sealed interface restricts implementations only to those defined in the same module (or the same file if the interface is not top-level). This type of control is applied for safe enum-like structures, the ADT pattern, and state handlers. The compiler knows all implementations and helps with code analysis.

Code example:

sealed interface NetworkResult class Success(val data: String): NetworkResult class Error(val cause: Throwable): NetworkResult object Loading: NetworkResult fun handleResult(result: NetworkResult): String = when (result) { is Success -> "Success with ${result.data}" is Error -> "Error: ${result.cause.message}" Loading -> "Loading..." }

Key features:

  • Sealed interfaces provide a limit on the number of implementations to improve type-safety.
  • They work with when expressions: the compiler checks for exhaustiveness.
  • Implementations can be both classes and object instances and other sealed types, but only within the same module.

Trick questions.

Can a sealed interface be implemented outside the current file?

Answer: Unlike sealed classes, sealed interfaces can be implemented in other files, but only within the current module (or compilation unit if the interface is not top-level).

Can sealed interfaces have open methods with default implementations?

Yes, like regular interfaces, a sealed interface can contain default function implementations.

sealed interface Mode { fun description(): String = "Unknown mode" }

Is it possible to serialize a sealed interface using standard serializers (for example, kotlinx.serialization)?

Yes, but all implementations need to be explicitly specified. Support for sealed interfaces in kotlinx.serialization did not appear immediately; it is important to explicitly specify serializable types.

Common mistakes and anti-patterns

  • Defining implementations outside the module
  • Excessive use of sealed interfaces instead of enums when there are fewer options and the structure is simpler
  • Not performing exhaustiveness checks when updating a sealed interface

Real-life example

Negative case

A sealed interface for all types of UI states was described in the project, but implementations began to appear in different parts of the application. Later, a new state type was added, and the handling block was forgotten to be updated, leading to the new state being ignored in the logs.

Pros:

  • Quick addition of new states

Cons:

  • Type-safety drops, unconsidered branches of logic appear

Positive case

A sealed interface was used for all network responses. Because of this, when adding a new response type, the IDE immediately highlighted all places where the handling is switched using when. The error is corrected right away, with no unexpected gaps in the logic.

Pros:

  • Safe refactoring
  • It is impossible to forget about the new state type

Cons:

  • In large structures, limiting the number of options may lead to increased maintenance