Kotlinでenumの序数を定義するとき、脳死でordinalを使うのは危険
enumの序数って?
Javaを例に使います。誰しも見たことがあるであろう、int型で1
とか定義してるアレです。[1]
public enum Region {
HOKKAIDO(0),
KANTO(1),
KANSAI(2);
private final int value;
Region(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
序数は以下のようにして取得できます。
System.out.println(Region.HOKKAIDO.getValue()); // 0
Kotlinならordinalを使え
Kotlinでenumの序数を使うとなったとき、先のJavaのように序数を定義してもよいのですがよく使うパターンなうえ、毎度コンストラクタを定義したりするのは非常に面倒です。
そこでKotlinには便利なプロパティが用意されています。ordinal
です。さっきのJavaと同じコードをKotlinで表現するとこうです。
enum class Region {
HOKKAIDO,
KANTO,
KANSAI;
}
序数は以下のようにして取得できます。
println(Region.HOKKAIDO.ordinal) // 0
めちゃくちゃシンプル。書くのめっちゃ楽 👏
ただし、脳死でordianlを使うのは危険
めちゃくちゃシンプルで使い勝手がよさそうに見えるordinal
ですが、扱いに注意が必要な場面があります。
それは序数に意味を持っている場合です。具体的な場面で言うと、序数をそのまま区分としてDBに保存するような場面です。
先のenum・Region
のそれぞれのordinal
は以下のようになっています。
enum class Region {
HOKKAIDO, // 0
KANTO, // 1
KANSAI; // 2
}
これらのordinal
の値をそのままDBに登録して保持しているというシチュエーションで、新たにTOKAI
という地域を追加する必要が出てきました。
この場合、日本人の感覚的にはKANTO
の後に入れたくなるのですが、今回のようなシチュエーションでそれをするとデータに不整合が発生してしまいます。
enum class Region {
HOKKAIDO, // 0
KANTO, // 1
TOKAI, // 2
KANSAI; // 3
}
上記のように元々関西を意味していた2
が東海を意味するようになってしまったため、DBにパッチを当てる必要が出てきてしまいました。
よって、列挙順に意味を持っている場合は諦めてenumの最後に追加するか、どうしても順序を入れ替えたい場合はDBにパッチを当てる必要が出てきてしまいます。ぱっと思いつく手段としてはこれら2つありましたが、あまりいい方法とは思えないです。
というわけで、enumで序数を扱うのにordianalを使うのはめっちゃいいんですが、列挙順そのものに意味を持つ場合は最初から素直に別で値を持った方がよさそうです。
おまけ
Kotlinのenumにはもう一つ便利なプロパティがあって、それがname
です。enumの定義そのものを文字列で取得することができます。
以下のようなイメージです。
enum class Region {
HOKKAIDO,
KANTO,
TOKAI,
KANSAI;
}
fun main() {
println("${Region.HOKKAIDO.name}, ${Region.HOKKAIDO.ordinal}")
println("${Region.KANTO.name}, ${Region.KANTO.ordinal}")
println("${Region.TOKAI.name}, ${Region.TOKAI.ordinal}")
println("${Region.KANSAI.name}, ${Region.KANSAI.ordinal}")
}
これは以下のように出力されます。
HOKKAIDO, 0
KANTO, 1
TOKAI, 2
KANSAI, 3
まとめ
基本的にordinal
、name
は活用すべき。けどenumの列挙順に意味を持つのであれば、ordinal
とは別の値を持った方がよい。
-
JavaもEnumは
ordinal()
メソッドを提供しているが、「EnumSetやEnumMapなどの高度なenumベースのデータ構造で使用するために設計されている」とあるため実質非推奨となっているっぽい。https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Enum.html#ordinal-- ↩︎
Discussion