Kotlin Sealed Classes (密封類別)
Sealed Class (密封類別) 是一種特殊的類別,用來表示 受限的繼承結構 (Restricted Class Hierarchies)。簡而言之,它就像是超級強大的 Enum。
當一個類別被標記為 sealed 時,它所有的子類別都必須定義在同一個檔案中(Kotlin 1.5 之後放寬到同一個編譯模組/套件中)。這讓編譯器能確切知道:除了這幾個子類別,再也沒有其他的了。
為什麼需要 Sealed Class?
雖然 Enum 可以定義常數,但每個 Enum 常數只能有同一組參數。而 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),所以編譯器知道所有的可能性。
Sealed Class 最強大的地方在於搭配 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))。
什麼時候該用 Sealed Classes?
只要你的資料結構有「這類東西只有這幾種可能」的特性,且每一種可能需要的資訊不同時,Sealed Class 就是最佳選擇。