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.
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.
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:
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.
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:
Cons:
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:
Cons: