Use when writing or reviewing Kotlin branching and control flow: when expressions, guard conditions, sealed type exhaustiveness, smart casts, nullable branching, early returns, or replacing complex if/else chains.
when subject.// Replace repeated checks against `state` with a subject `when`.
val action = when (state) {
State.SignedOut -> Action.ShowSignIn
is State.SignedIn -> Action.ShowHome(state.user)
}
when or an if chain.| If the code has... | Use... |
|---|---|
| One value being classified | when (subject) |
| Unrelated boolean conditions | Subjectless when or if/else |
| A primary match plus an extra branch-local predicate | Guard condition |
| Invalid input before the main path | Early return, require, or check |
| A closed enum, Boolean, sealed type, or nullable closed type returning a value | Exhaustive when expression |
| Open external input or a real fallback | Explicit else |
return when (event) {
is Event.Message if event.isUnread -> Row.Highlighted(event.message)
is Event.Message -> Row.Normal(event.message)
Event.Empty -> Row.Empty
}
when has a subject.is Type, enum entry, object, value, range, etc.).when expression over a closed domain, handle every case explicitly. Do not add else only to quiet the compiler.val action = when (state) {
SessionState.SignedOut -> Action.ShowSignIn
is SessionState.SignedIn -> Action.ShowHome(state.user)
is SessionState.Expired if state.canRefresh -> Action.Refresh
is SessionState.Expired -> Action.ShowSignIn
}
else when the domain is open: strings from a server, integer status codes, unknown platform values, or a deliberate fallback/logging path.when (status) {
Status.Pending if canRetry -> retry()
Status.Pending -> showPending()
Status.Queued -> showQueued()
}
fun render(user: User?): UiModel {
user ?: return UiModel.SignedOut
return UiModel.SignedIn(
name = user.name,
avatar = user.avatar,
)
}
as, !!, temporary mutable vars, or duplicated casts, keep the original shape or choose a smaller refactor.when// Before
return when (event) {
is Event.Message -> {
if (event.isUnread) Row.Highlighted(event.message) else Row.Normal(event.message)
}
Event.Empty -> Row.Empty
}
// After
return when (event) {
is Event.Message if event.isUnread -> Row.Highlighted(event.message)
is Event.Message -> Row.Normal(event.message)
Event.Empty -> Row.Empty
}
// Before
return when {
result is Result.Success -> Ui.Success(result.value)
result is Result.Failure && result.canRetry -> Ui.Retry(result.error)
result is Result.Failure -> Ui.Error(result.error)
else -> Ui.Loading
}
// After
return when (result) {
is Result.Success -> Ui.Success(result.value)
is Result.Failure if result.canRetry -> Ui.Retry(result.error)
is Result.Failure -> Ui.Error(result.error)
Result.Loading -> Ui.Loading
}
when (value) when null is one branch in a larger classification:return when (val selected = selection) {
null -> SelectionUi.None
is Selection.Single if selected.item.isArchived -> SelectionUi.Archived(selected.item)
is Selection.Single -> SelectionUi.Active(selected.item)
is Selection.Multiple -> SelectionUi.Count(selected.items.size)
}
when expressions remain exhaustive without unnecessary else.as, !!, or duplicated casts.when.else for open-world external input.kotlin-flow-state-event-modeling - flow state and event primitive choices.kotlin-multiplatform-expect-actual - keeping business branching in common code and platform actuals thin.