Sealed Classes and Sealed Interfaces

In Kotlin, Sealed classes and interfaces are used to represent restricted class hierarchies. They allow you to define a fixed set of types, providing more control over inheritance and enabling powerful features like exhaustive when expressions.

Think of them as "Enums on steroids." While an Enum constant is just a single instance, a subclass of a sealed class can have multiple instances and hold its own unique state.


1. Sealed Class

sealed class is an abstract class that can only be subclassed within the same package/module where it is defined.

  • State: Each subclass can have its own properties and methods.
  • Constructor: It can have a constructor (private by default) to pass data to subclasses.
  • Usage: Best used when your types share a common base with some logic or state.

2. Sealed Interface

Introduced in Kotlin 1.5, sealed interface works exactly like a sealed class but follows interface rules.

  • No State: It cannot hold properties with backing fields (state).
  • Multiple Inheritance: A class can implement multiple interfaces, but can only extend one class.
  • Usage: Best used when you want to define a common behavior across different hierarchies or when you don't need a constructor.

How to Use Them

The most common use case is representing UI State or Result types.

Code Example: Network Result


sealed interface DataResult {
    data class Success(val data: String) : DataResult
    data class Error(val message: String) : DataResult
    object Loading : DataResult
}

fun handleResult(result: DataResult) {
    // The compiler knows all possible types of DataResult
    // No 'else' branch is required!
    when (result) {
        is DataResult.Success -> println("Received: ${result.data}")
        is DataResult.Error -> println("Error: ${result.message}")
        DataResult.Loading -> println("Loading...")
    }
}

Key Differences

FeatureSealed ClassSealed Interface
ConstructorYes (can pass parameters)No
InheritanceSingle inheritance onlySupports multiple implementations
Backing FieldsCan have stored propertiesOnly abstract properties/methods
Primary GoalRestricted hierarchy with shared stateRestricted hierarchy for behavior/types

Why Use Them?

  1. Exhaustive when: The compiler will warn you if you forget to handle one of the subclasses. This prevents bugs when adding new types later.
  2. Type Safety: You know exactly which types belong to the hierarchy, making the code predictable.
  3. Domain Modeling: Perfect for representing states like SuccessErrorPending, or Idle.

Comments

Popular posts from this blog

What is a Coroutine? Why is it better than threads?

What are Coroutine Builders?