sealed階層を網羅するwhen
すべてのsealedバリアントを網羅する完全なwhen式を記述します。
「sealed階層を網羅するwhen」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。
網羅的な when
式として使用する when 式は網羅的である必要があります。つまり、考えられるすべての値を処理しなければなりません。sealed 階層を使うと、コンパイラーがこれを検証してくれます。
オープンな型での非網羅的な when
通常のクラスでは、コンパイラーはすべてのサブタイプを確認できないため、else ブランチが必要です。
open class Shape
class Circle : Shape()
class Square : Shape()
fun describe(s: Shape) = when (s) {
is Circle -> "circle"
is Square -> "square"
else -> "unknown" // required
}
fun main() { println(describe(Circle())) }sealed 型での網羅的な when
sealed class では、コンパイラーがすべてのサブタイプを把握しています。すべてのケースを網羅していれば、else は不要です。
sealed class Result
class Success(val value: Int) : Result()
class Failure(val msg: String) : Result()
fun describe(r: Result) = when (r) {
is Success -> "ok: ${r.value}"
is Failure -> "err: ${r.msg}"
}
fun main() {
println(describe(Success(42)))
println(describe(Failure("nope")))
}sealed interface
sealed interface を使うと、クラスが多重継承を必要とする場合でも網羅性を検証できます。
sealed interface Animal
class Dog : Animal
class Cat : Animal
fun speak(a: Animal) = when (a) {
is Dog -> "woof"
is Cat -> "meow"
}
fun main() {
println(speak(Dog()))
println(speak(Cat()))
}ブランチでのスマートキャスト
各 is ブランチ内では、Kotlin が変数を特定の型にスマートキャストするため、手動でキャストする必要はありません。
sealed class Event
data class Click(val x: Int, val y: Int) : Event()
data class Key(val char: Char) : Event()
fun handle(e: Event) = when (e) {
is Click -> "clicked at (${e.x},${e.y})" // smart-cast
is Key -> "key=${e.char}"
}
fun main() {
println(handle(Click(10, 20)))
println(handle(Key('A')))
}ケースの記述漏れ
後から新しいバリアントを追加すると、sealed 型を式として使用しているすべての網羅的な when がコンパイルに失敗します。これは非常に有効な安全策です。
sealed class Status
object Idle : Status()
object Running : Status()
// If we add object Done : Status(), every exhaustive when must update.
fun text(s: Status): String = when (s) {
is Idle -> "idle"
is Running -> "running"
}
fun main() { println(text(Idle)) }data class を使った sealed 型
よく使われるパターンは、sealed 階層のバリアントとして data class を使用することです。
sealed class UiState {
object Loading : UiState()
data class Success(val data: List<String>) : UiState()
data class Error(val message: String) : UiState()
}
fun render(state: UiState) = when (state) {
UiState.Loading -> "loading..."
is UiState.Success -> "got ${state.data.size} items"
is UiState.Error -> "error: ${state.message}"
}
fun main() {
println(render(UiState.Loading))
println(render(UiState.Success(listOf("a", "b"))))
println(render(UiState.Error("404")))
}when をステートメントとして使う
ステートメントとして使用する場合(式ではない場合)、when は網羅的である必要がありません。ただし、安全策を失うことになります。
sealed class Cmd
object Start : Cmd()
object Stop : Cmd()
fun execute(c: Cmd) {
// Statement form: no error if incomplete (avoid this style)
when (c) {
Start -> println("started")
Stop -> println("stopped")
}
}
fun main() { execute(Start); execute(Stop) }ステートメントで網羅性チェックを強制する
when を値に代入するか、.exhaustive のテクニックを使って、コンパイラーにすべてのケースを検証させます。
sealed class Cmd
object Start : Cmd()
object Stop : Cmd()
fun execute(c: Cmd) {
val handled: Unit = when (c) { // expression context now
Start -> println("started")
Stop -> println("stopped")
}
}
fun main() { execute(Start) }複数ケースのブランチ
カンマでケースをまとめると、複数のバリアントで同じ本体を共有できます。
sealed class Event
class Click : Event()
class Tap : Event()
class Drag : Event()
fun isPointer(e: Event) = when (e) {
is Click, is Tap, is Drag -> true
}
fun main() { println(isPointer(Tap())) }ネストされた sealed 型
sealed 階層はネストできます。深いツリーに対しても網羅性チェックは機能します。
sealed class Network {
sealed class Wifi : Network() {
object Open : Wifi()
data class Secured(val ssid: String) : Wifi()
}
object Cellular : Network()
}
fun describe(n: Network) = when (n) {
Network.Wifi.Open -> "open wifi"
is Network.Wifi.Secured -> "secured: ${n.ssid}"
Network.Cellular -> "cellular"
}
fun main() {
println(describe(Network.Wifi.Secured("home")))
println(describe(Network.Cellular))
}クイックチェック
sealed 階層と when を組み合わせる主な利点は何ですか?
まとめ
sealed class や sealed interface と when を組み合わせて、コンパイル時の網羅性を確保します。スマートキャストによってブランチ本体を簡潔に記述できます。式として使用するか、結果を代入してチェックを強制し、カンマでケースをまとめ、ツリー状の状態には sealed 型をネストしてください。
よくある質問
「sealed階層を網羅するwhen」レッスンは無料ですか?
はい。「sealed階層を網羅するwhen」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。
「sealed階層を網羅するwhen」で何を学びますか?
すべてのsealedバリアントを網羅する完全なwhen式を記述します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Kotlin Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「sealed階層を網羅するwhen」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このKotlin Academyレッスンでコードを書いて実行できますか?
はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。