Kotlin Sealed Classes
A sealed class is a restricted class hierarchy — all subclasses must be defined in the same file. This gives you a fixed set of possible types. Sealed classes are ideal for representing states, results, or events where every possibility is known in advance.
Defining a Sealed Class
sealed class PaymentResult {
data class Success(val transactionId: String, val amount: Double) : PaymentResult()
data class Failure(val errorCode: Int, val reason: String) : PaymentResult()
object Pending : PaymentResult()
}
fun processPayment(result: PaymentResult) {
when (result) {
is PaymentResult.Success -> println("Paid ₹${result.amount} | Txn: ${result.transactionId}")
is PaymentResult.Failure -> println("Failed [${result.errorCode}]: ${result.reason}")
is PaymentResult.Pending -> println("Payment is being processed...")
}
}Why Sealed Classes Are Powerful
Sealed class guarantees ALL cases are covered:
when (result) {
is Success → handle
is Failure → handle
is Pending → handle
// No "else" needed — compiler verifies all cases!
}
With a regular class:
when (result) {
// What if someone adds a new subclass in another file?
// Your when might miss it. Bug!
}
Sealed Class as a State Machine
sealed class NetworkState {
object Idle : NetworkState()
object Loading : NetworkState()
data class Success(val data: String) : NetworkState()
data class Error(val message: String) : NetworkState()
}
fun render(state: NetworkState): String = when (state) {
NetworkState.Idle -> "Waiting for request..."
NetworkState.Loading -> "Loading, please wait..."
is NetworkState.Success -> "Data: ${state.data}"
is NetworkState.Error -> "Error: ${state.message}"
}
fun main() {
val states = listOf(
NetworkState.Idle,
NetworkState.Loading,
NetworkState.Success("User list loaded"),
NetworkState.Error("Connection timeout")
)
states.forEach { println(render(it)) }
}Output:
Waiting for request...
Loading, please wait...
Data: User list loaded
Error: Connection timeoutSealed Class Diagram
PaymentResult (sealed)
│
┌───────┼───────┐
│ │ │
Success Failure Pending
(data (data (object
class) class) singleton)
Sealed Class vs Enum
Enum Sealed Class
─────────────────────────────────────────────────────
Each variant holds data? NO (shared) YES (own fields)
Each variant is different type? NO YES
Subclassing? NO YES
Pattern matching? YES YES
Practical Example: Form Validation
sealed class ValidationResult {
object Valid : ValidationResult()
data class Invalid(val errors: List) : ValidationResult()
}
fun validateEmail(email: String): ValidationResult {
val errors = mutableListOf()
if (!email.contains("@")) errors.add("Missing @ symbol")
if (!email.contains(".")) errors.add("Missing domain extension")
if (email.length < 5) errors.add("Email too short")
return if (errors.isEmpty()) ValidationResult.Valid
else ValidationResult.Invalid(errors)
}
fun main() {
val emails = listOf("alice@mail.com", "notanemail", "x@y")
emails.forEach { email ->
when (val result = validateEmail(email)) {
ValidationResult.Valid -> println("✓ $email is valid")
is ValidationResult.Invalid -> {
println("✗ $email is invalid:")
result.errors.forEach { println(" - $it") }
}
}
}
} Output:
✓ alice@mail.com is valid
✗ notanemail is invalid:
- Missing @ symbol
✗ x@y is invalid:
- Email too short