Kotlin 1.8.20 新改動
當我們建立函式/類別的時候,為了要儲存狀態,可能會直接使用基本型別,如下 GameScore
類別中,包含了一個型別為 String
的等級(grade):
class GameScore(val grade: String)
使用這個類別的時候,我們必須將型別為 String
的等級存入,如下:
GameScore("A")
GameScore("S")
但是這麼做會有風險,因為有可能會有人不小心存入我們不接受的內容,如下:
GameScore("100") // ❌
所以為了要讓程式更好維護,讓呼叫端知道我們能接受的輸入值,在這邊我們可以選擇使用 Enum 。
ℹ️ 透過 Enum 我們可以將基本型別轉換成更具有識別度的物件,而 Kotlin 在 1.6 版本也已經在
when
上支援窮舉 Enum,這讓我們使用 Enum 更方便(在這之前只有 sealed class 能夠在when
裡面被窮舉。)
針對 GameScore 類別,我們建立了一個 enum class
Grade ,在這個 Enum 裡面,除了定義了其 Enum 值,還有一個屬性(String):
enum class Grade(val display: String) {
S("S"),
A_Plus("A+"),
A("A"),
A_Minus("A-"),
B_Plus("B+"),
B("B"),
B_Minus("B-"),
}
於是,我們可以將 GameScore 改成:
class GameScore(val grade: Grade)
而在呼叫時,就只能使用存在於 enum class Grade
的值:
GameScore(Grade.S)
GameScore(Grade.A)
當使用其他不合法的值時,就會在編譯期發生錯誤,如此就可以減少許多不經意的錯誤。
Enum.values()
使用 Enum 時,若需要列舉所有的 Enum 值,可以使用 values()
函式。如下,若我們希望列印出所有 enum class Grade
裡的 display 值,透過 Kotlin 簡潔的語法,只需要一行就可以了:
Grade.values().forEach { println(it.display) }
S
A+
A
A-
B+
B
B-
Enum.entries
在 Kotlin 1.8.20 中,在 Enum 中加入了一個屬性 entries
*,而這個屬性同樣也包含了所有 Enum 類的所有內容,我們可以使用 entries
改寫上面的範例:
Grade.entries.forEach { println(it.display) }
如無意外,我們可以得到相同的結果:
S
A+
A
A-
B+
B
B-📝 在 Kotlin 1.8.20 中,
Enum.entries
仍然還不是正式推出,所以必須要加上@OptIn(ExperimentalStdlibApi::class)
才能夠使用。
既然已經有了 values()
為什麼還需要 entries
屬性呢?
既生瑜,何生亮
Enum.values()
的用途是建立一個新的 Array<Grade>
。Array<T>
是一個可變的陣列,在每次呼叫的時候,都會建立一個 Array<Grade>
,如果在 Enum 內部有許多內容時,就會分配許多記憶體,呼叫愈多多餘的記憶體被使用。 另外,在 Kotlin 內會使用 Collection 來取代 Array,因為在 Collection 具有許多現代化的呼叫方式,當然最重要的是比較省資源。而 Enum.entries
的型別是 EnumEntries<E : Enum<E>>
,而其父類別是 List<E>
,可以發現它是一個不可變(Inmutable)的列表。
public sealed interface EnumEntries<E : Enum<E>> : List<E>
結論
在 Kotlin 1.8.20 加入 Enum.entries
之前,我還真的不知道每次使用 Enum.values()
時,會發生這些隱藏的效能問題,對開發者來說,每次消除一些記憶體的浪費,累積起來也是很可觀的,雖然這個改動可能感受不是那麼的明顯,不過為了避免因為應用的記憶體不夠,而出現一些不可預測的問題,我想這個改動還是值得的。