[Kotlin 小撇步 #11] 使用 Enum.entries 取代 Enum.values()

Andy Lu
4 min readApr 25, 2023

Kotlin 1.8.20 新改動

Image by Andy Bay from Pixabay

當我們建立函式/類別的時候,為了要儲存狀態,可能會直接使用基本型別,如下 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() 時,會發生這些隱藏的效能問題,對開發者來說,每次消除一些記憶體的浪費,累積起來也是很可觀的,雖然這個改動可能感受不是那麼的明顯,不過為了避免因為應用的記憶體不夠,而出現一些不可預測的問題,我想這個改動還是值得的。

--

--

Andy Lu

Android/Flutter developer, Kotlin Expert, like to learn and share.