Careeroza
Exhaustive when
The Compiler Checks Every Case
Because all subclasses are known, a when expression over a sealed type needs no else. If someone adds a new subclass, every when that does not handle it stops compiling, so you can never forget a case.
Example
sealed class PaymentResult {
data class Success(val txnId: String) : PaymentResult()
data class Failure(val reason: String, val retryable: Boolean) : PaymentResult()
data object Pending : PaymentResult()
}
fun message(r: PaymentResult) = when (r) {
is PaymentResult.Success -> "Paid! Transaction ${r.txnId}"
is PaymentResult.Failure -> if (r.retryable) "Failed: ${r.reason}. Try again." else "Failed: ${r.reason}"
PaymentResult.Pending -> "Waiting for bank confirmation..."
}
fun main() {
listOf(
PaymentResult.Success("TXN7781"),
PaymentResult.Failure("Insufficient balance", false),
PaymentResult.Failure("Bank server timeout", true),
PaymentResult.Pending,
).forEach { println(message(it)) }
}Output
Paid! Transaction TXN7781 Failed: Insufficient balance Failed: Bank server timeout. Try again. Waiting for bank confirmation...
Exercise
Exercise: Model a login result as a sealed class with LoggedIn(userName), WrongPassword(attemptsLeft) and Blocked, and write a when that returns a message for each.
Show answer
sealed class LoginResult {
data class LoggedIn(val userName: String) : LoginResult()
data class WrongPassword(val attemptsLeft: Int) : LoginResult()
data object Blocked : LoginResult()
}
fun msg(r: LoginResult) = when (r) {
is LoginResult.LoggedIn -> "Welcome ${r.userName}"
is LoginResult.WrongPassword -> "Wrong password, ${r.attemptsLeft} attempts left"
LoginResult.Blocked -> "Account blocked"
}