Kotlin Sealed Classes (密封類別)

Sealed Class (密封類別) 是一種特殊的類別,用來表示 受限的繼承結構 (Restricted Class Hierarchies)。 簡而言之,它就像是 超級強大的 Enum

為什麼需要 Sealed Class?

Enum 雖然好用,但有個限制:每個 Enum 常數都只能是單例 (Singleton),無法攜帶自定義的狀態。 Sealed Class 允許每個子類別擁有自己的屬性、狀態,甚至是多個實例。

定義 Sealed Class

最常見的應用場景是用來定義 UI 狀態 (UI State)

sealed class UiState {
    // 單例狀態:載入中 (不需要帶資料)
    object Loading : UiState()
    
    // 成功狀態:攜帶資料 List<String>
    data class Success(val data: List<String>) : UiState()
    
    // 錯誤狀態:攜帶錯誤訊息 Exception
    data class Error(val exception: Exception) : UiState()
}

搭配 when 使用

因為 Sealed Class 的子類別是 有限的 (Known at compile time),所以編譯器知道所有的可能性。 當你在 when 表達式中使用時,不需要寫 else 分支 (前提是你覆蓋了所有情況)。這讓程式碼更安全,因為如果你新增了一個狀態但忘了處理,編譯器會報錯。

fun handleState(state: UiState) {
    when (state) {
        is UiState.Loading -> {
            println("Loading...")
        }
        is UiState.Success -> {
            println("Got data: ${state.data}")
        }
        is UiState.Error -> {
            println("Error: ${state.exception.message}")
        }
        // 不需要 else!
    }
}

Sealed Interface

從 Kotlin 1.5 開始,你也使用 sealed interface。 如果你不需要繼承任何實作邏輯,只想要定義型別階層,sealed interface 會比 sealed class 更輕量且更有彈性 (因為類別只能單一繼承,但介面可以多重實作)。

sealed interface Result
data class Success(val value: Int) : Result
data class Failure(val error: String) : Result

總結:Enum vs Sealed Class

  • Enum: 狀態是固定的常數,不能帶動態資料 (例如 Color.RED, Direction.NORTH)。
  • Sealed Class: 狀態是有限的類別集合,每個狀態可以攜帶不同的資料實例 (例如 Success(data), Error(msg))。