列舉類別與密封類別#
列舉類別(Enum Class) 用於定義一組固定的常數;密封類別(Sealed Class) 則用於定義一組有限的子類別,兩者都常與 when 搭配使用。
1. 列舉類別(Enum Class)#
基本宣告#
1
2
3
4
5
6
7
8
| enum class Direction {
NORTH, SOUTH, EAST, WEST
}
fun main() {
val dir = Direction.NORTH
println(dir) // 輸出:NORTH
}
|
搭配 when 使用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| enum class Direction {
NORTH, SOUTH, EAST, WEST
}
fun describe(dir: Direction): String {
return when (dir) {
Direction.NORTH -> "往北"
Direction.SOUTH -> "往南"
Direction.EAST -> "往東"
Direction.WEST -> "往西"
}
}
fun main() {
println(describe(Direction.EAST)) // 輸出:往東
}
|
使用 when 搭配列舉時,編譯器會確認所有分支都已處理,不需要 else。
列舉帶屬性#
每個列舉常數可以攜帶屬性值。
1
2
3
4
5
6
7
8
9
10
| enum class Planet(val mass: Double, val radius: Double) {
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6),
EARTH(5.976e+24, 6.37814e6)
}
fun main() {
val earth = Planet.EARTH
println("地球質量:${earth.mass}") // 輸出:地球質量:5.976E24
}
|
列舉的內建屬性與方法#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| enum class Color { RED, GREEN, BLUE }
fun main() {
val c = Color.GREEN
println(c.name) // 輸出:GREEN(名稱字串)
println(c.ordinal) // 輸出:1(索引,從 0 開始)
// 從字串取得列舉值
val parsed = Color.valueOf("BLUE")
println(parsed) // 輸出:BLUE
// 取得所有列舉值
Color.entries.forEach { println(it) }
// 輸出:RED GREEN BLUE
}
|
2. 密封類別(Sealed Class)#
- 密封類別的所有子類別必須定義在 同一個套件(package)中。
- 編譯器能掌握所有子類別,與
when 搭配時可提供窮舉檢查。 - 子類別可以是
class、data class、object,可攜帶不同的資料。
基本宣告#
1
2
3
4
5
| sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
}
|
搭配 when 使用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
}
fun handle(result: Result) {
when (result) {
is Result.Success -> println("成功:${result.data}")
is Result.Error -> println("錯誤:${result.message}")
Result.Loading -> println("載入中…")
}
}
fun main() {
handle(Result.Success("使用者資料")) // 輸出:成功:使用者資料
handle(Result.Error("網路逾時")) // 輸出:錯誤:網路逾時
handle(Result.Loading) // 輸出:載入中…
}
|
實際應用:網路請求狀態#
密封類別最常見的用途是表示有限的狀態集合,例如 API 請求的結果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| sealed class ApiResponse {
data class Success(val body: String) : ApiResponse()
data class HttpError(val code: Int, val message: String) : ApiResponse()
object NetworkFailure : ApiResponse()
}
fun processResponse(response: ApiResponse) {
when (response) {
is ApiResponse.Success ->
println("回應內容:${response.body}")
is ApiResponse.HttpError ->
println("HTTP 錯誤 ${response.code}:${response.message}")
ApiResponse.NetworkFailure ->
println("無法連線至伺服器")
}
}
fun main() {
processResponse(ApiResponse.Success("{ \"status\": \"ok\" }"))
processResponse(ApiResponse.HttpError(404, "Not Found"))
processResponse(ApiResponse.NetworkFailure)
// 輸出:
// 回應內容:{ "status": "ok" }
// HTTP 錯誤 404:Not Found
// 無法連線至伺服器
}
|
3. 列舉類別 vs. 密封類別#
| 比較項目 | 列舉類別 | 密封類別 |
|---|
| 每個常數的資料 | 共用相同屬性結構 | 各子類別可有不同屬性 |
| 實例數量 | 每個常數只有一個實例 | 子類別可建立多個實例 |
| 適用場景 | 固定常數集合(方向、顏色、狀態碼) | 有限的資料型別集合(網路結果、UI 狀態) |
Reference#