在 Java 中當我們需要定義多個相同型別的時候,我們就可以使用 enum 列舉。
而在 Kotlin 定義一個列舉是使用 enum class。
例如:假設我們寫一個影片播放器,我們可以針對該影片播放器定義一組 enum class 來設定播放器的狀態。
如何使用呢?假設有一個函式會根據 State 的 enum 值來列印適當的內容。
如果今天我們希望在 enum class 中帶入其它的屬性。
例如:我們想利用 enum class 來列出特斯拉的所有型號包括其座位數,我們可以這樣寫。
當需要簡單的型別集合時,我們就可以使用 enum 列舉。
So far so good, but…
- 在 enum class 的每一個成員,都必須要寫在 enum class 裡頭;也就是說,enum class 必須要把所有的成員寫在一起。
- 在 enum class 裡的每一個成員,其屬性必須要通通相同;也就是說無法針對特定的成員設定特定的屬性。
sealed class (密封類別)
首先來看看 sealed class 是怎麼定義的,我將上面的兩個例子改成以 sealed class 來實現:
在 class 前方加上關鍵字 sealed ,這個 class 就成為了sealed class。
sealed class 有什麼特性呢?
- 可以限制子類別的繼承。
- 在 when 的使用上,可以詳盡的列出所有的子類別。
- 子類別可以定義在其他的位置(相同的 package name)- Kotlin 1.5 更新
可以限制子類別的繼承
所有 sealed class 的子類別在編譯時期,都是已知的,且在編譯完成之後就不允許其他的子類別繼承該 sealed class。例如,我們無法繼承第三方 lib 的 sealed class。
在 when 的使用上,可以詳盡的列出所有的子類別。
因為 sealed class 在編譯完成之後,就不能有其他的子類別,所以利用 when 來判斷 sealed class,就可以列出所有的子類別。
子類別可以定義在其他的位置
Kotlin 1.4.30 將此功能設為 Experimental,並在 1.5 版正式推出此優化。
在這之前,sealed class 雖然是 class,但是各個子類別卻只能寫在同一隻檔案中,如果子類別的內容比較多,那麼就會讓整個檔案的大小也跟著增加,所以現在可以在不同的檔案中繼承同一個 sealed class/sealed interface,真是太好了 XD
sealed interfaces (密封介面)
Kotlin 1.5 重大的更新之一 sealed interfaces。如同原本的 interfaces,一個類別可以繼承多個 interfaces ,所以一個類別同樣也可以繼承多個 sealed interfaces,當這麼做,這麼做有什麼好處呢?
假設有兩個 sealed interface ,一個是 Animal (動物),另一個為 Carnivore (肉食性)。
class Lion 實作 Animal 以及 Carnivore 兩個 sealed interface,
object Tiger 實作 Animal 以及 Carnivore 兩個 sealed interface,
object Bird 實作 Animal sealed interface。
與 sealed class 相同,在 printAnimal 中,我們可以用 when 來判斷所有實作 Animal sealed interface 的 子類別;在 printCarnivore 中,我們同樣也可以使用 when 來判斷所有實作 Carnivore interface 的子類別。
藉由 interface 的特性,我們可以讓類別可以達成一對多個 sealed interface,來使用 sealed 關鍵字的特性。
enum class 實作 sealed interface
在 Kotlin 中,enum 是一個 class ,但是它只能夠實作 interface 而不能夠繼承 class。
enum class 實作 interface 有什麼作用呢?
當實作某一 interface 時,每一個 enum class 的成員就必須要實作該 interface 的 函式。
那如果繼承的是 sealed interface 呢?
enum class 繼承 sealed interface 之後,該 enum class 就屬於該 sealed interface 的子類別,當用 when 判斷該 sealed interface 時,該 enum class 的 成員都必須要被判斷到。
結論
跟原本的 class 不同,sealed class 能夠限制子類別的繼承,在編譯完成之後就不能夠被繼承,所以用 when 判斷時,能夠列出所有的子類別。
Kotlin 1.5 版中,子類別不需要與 sealed class 在同一隻檔案中,只需要在同一個 package name 底下即可,也就是說,可以將子類別拆到不同的檔案中,依此減少檔案的大小。
另一個重點 1.5 版的新增項目 sealed interface ,讓原本子類別只能繼承一個 sealed class 改成可以實作多個 sealed interfaces,用來增加子類別的可用性。
sealed interface 還可以被 enum class 實作,當被 enum class 實作後,利用 when 判斷 sealed interface ,就可以連該 enum class 的所有成員也都被判斷到。
最後有一個小地方,在用 when 判斷的時候,判斷 enum 需要將 enum 的名稱寫在前方,而 sealed class 則不需要,如此也可以減少程式碼數量 XD