Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

Kotlin辞典

  1. トップページ
  2. Kotlin辞典
  3. sealed class / sealed interface

sealed class / sealed interface

Kotlinの『sealed class』は、継承できるサブクラスを同じファイル内(またはネスト)に限定したクラスです。『when』式と組み合わせると網羅性チェックができ、状態管理や結果型の実装に活用されます。

構文
// sealed class の定義
sealed class Result<out T>
data class Success<T>(val data: T) : Result<T>()
data class Error(val message: String) : Result<Nothing>()
object Loading : Result<Nothing>()

// when 式で網羅的に処理します(else 不要)
fun handle(result: Result<String>) = when (result) {
    is Success -> println("成功: ${result.data}")
    is Error   -> println("エラー: ${result.message}")
    is Loading -> println("読み込み中...")
}

// sealed interface(Kotlin 1.5以降)
sealed interface Shape
data class Circle(val radius: Double) : Shape
data class Rectangle(val w: Double, val h: Double) : Shape
構文一覧
構文概要
sealed class 名前同じコンパイルユニット内にのみサブクラスを持てるクラスです。
sealed interface 名前Kotlin 1.5以降で使える sealed インターフェースです。
is サブクラス名when 式での型チェックです。sealed class なら全ケース網羅で else 不要になります。
object サブクラス名 : 親()状態を持たないサブクラスをシングルトンとして定義します。
サンプルコード
// UI 状態管理のパターン
sealed class UiState<out T> {
    object Loading : UiState<Nothing>()
    data class Success<T>(val data: T) : UiState<T>()
    data class Error(val message: String, val code: Int = 0) : UiState<Nothing>()
}

// ネットワーク操作の模擬
fun fetchUser(id: Int): UiState<String> = when (id) {
    0 -> UiState.Loading
    in 1..100 -> UiState.Success("User-$id")
    else -> UiState.Error("ユーザーが見つかりません", 404)
}

// when 式で網羅的に処理します(コンパイラが全ケース確認)
fun render(state: UiState<String>): String = when (state) {
    is UiState.Loading -> "読み込み中..."
    is UiState.Success -> "データ: ${state.data}"
    is UiState.Error -> "エラー(${state.code}): ${state.message}"
}

// sealed class を使ったコマンドパターン
sealed class Command
data class Move(val dx: Int, val dy: Int) : Command()
data class Rotate(val degrees: Int) : Command()
object Stop : Command()

fun execute(cmd: Command) {
    when (cmd) {
        is Move -> println("移動: (${cmd.dx}, ${cmd.dy})")
        is Rotate -> println("回転: ${cmd.degrees}度")
        is Stop -> println("停止")
    }
}

fun main() {
    // UiState のデモ
    listOf(0, 1, 999).forEach { id ->
        val state = fetchUser(id)
        println(render(state))
    }

    println()

    // コマンドパターンのデモ
    val commands = listOf(Move(10, 5), Rotate(90), Move(-3, 2), Stop)
    commands.forEach { execute(it) }
}
概要

『sealed class』は、取り得る状態を型レベルで制限できるため、「Success/Error/Loading」のような状態機械、「Result型」、「代数的データ型(ADT)」の実装に適しています。

『when』式で全サブクラスを処理した場合、コンパイラが網羅性を検証します。新しいサブクラスを追加したとき、処理漏れがあればコンパイルエラーになるため、保守性が高まります(『when』を式として使うと網羅チェックが有効になります)。

クラスの基本はclass / コンストラクターを、スマートキャストはスマートキャスト / as?を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。