はじめに
Kotlin の sealed class(シールドクラス)は、継承範囲を制限できる特殊な抽象クラスです。
主に「状態」や「結果」などを安全に表現するために使われます。
enum class より柔軟で、abstract class より安全に状態を管理できるのが特徴です。
sealed class とは?
sealed class は「同じファイル内でのみ継承可能」なクラスです。
これにより、想定外のサブクラス定義を防ぎ、型の網羅性を保証できます。
基本構文
sealed class Result
class Success(val data: String) : Result()
class Error(val message: String) : Result()
object Loading : Result()
-
sealed class自体は abstract(抽象クラス) - サブクラスは 同じ Kotlin ファイル内 に定義する必要あり
- これにより、安全で予測可能な継承ツリーを構築できる
when 式との組み合わせが強力!
sealed class の最大のメリットは、when 式で 網羅性チェック ができることです。
fun handle(result: Result) = when (result) {
is Success -> println("成功: ${result.data}")
is Error -> println("エラー: ${result.message}")
Loading -> println("読み込み中…")
// else が不要! → コンパイラが全ケースを認識している
}
sealed class のすべての派生クラスを コンパイル時に把握できる ため、
else 分岐なしで安全な when 式が書けます。
ジェネリクスと組み合わせた実用例
API 呼び出し結果を表す「Result 型」を定義してみましょう。
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
object Loading : Result<Nothing>()
}
使い方例
fun loadUser(): Result<String> {
return try {
val user = "Anna"
Result.Success(user)
} catch (e: Exception) {
Result.Error(e)
}
}
fun showResult(result: Result<String>) = when (result) {
is Result.Success -> println("ユーザー名: ${result.data}")
is Result.Error -> println("エラー発生: ${result.exception.message}")
Result.Loading -> println("ロード中…")
}
→ Success / Error / Loading の3つの状態を安全に扱える構造。
sealed class と enum class の違い
| 比較項目 | sealed class | enum class |
|---|---|---|
| 定義の柔軟性 | 任意のクラス構造が定義できる | 固定値のみ |
| データ保持 | 可能(プロパティあり) | 不可(定数のみ) |
| 継承 | 可(同ファイル内のみ) | 不可 |
| when 式の網羅性 | あり | あり |
| 主な用途 | 状態表現、結果モデリング | 定数グループ化 |
sealed interface との違い
Kotlin 1.5 以降では、sealed interface も導入されました。
クラスに縛られず、より柔軟に状態やイベントを表現できます。
sealed interface UiState
data class Success(val data: String) : UiState
object Loading : UiState
object Error : UiState
sealed interface は「複数の型にまたがる状態管理」に便利です。
実践例:UI 状態の管理(MVVM / MVI)
例えば Android の ViewModel で使うとこうなります。
sealed class UiState {
object Loading : UiState()
data class Success(val items: List<String>) : UiState()
data class Error(val message: String) : UiState()
}
class UserViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState = _uiState.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
try {
val users = fetchUsers()
_uiState.value = UiState.Success(users)
} catch (e: Exception) {
_uiState.value = UiState.Error("読み込みに失敗しました")
}
}
}
}
これで UI 層は安全に状態を観察できます。
viewModel.uiState.collect { state ->
when (state) {
is UiState.Loading -> showLoading()
is UiState.Success -> showList(state.items)
is UiState.Error -> showError(state.message)
}
}
まとめ
| ポイント | 説明 |
|---|---|
| 継承範囲を制限 | 同一ファイル内のみ継承可 |
| 型安全 | when 式で網羅性チェック |
| 柔軟 | enum より柔軟に状態・結果を表現できる |
| 実用例 | API 結果、UI 状態、イベント管理など |
| 拡張機能 | Kotlin 1.5以降で sealed interface にも対応 |
sealed class は Kotlin の中でも特に「状態管理」や「エラー処理」を安全・明確に表現できる強力な仕組みです。
enum より柔軟に、abstract class より安全に。
アプリの状態設計をより堅牢にするために、ぜひ活用してみてください。