🫖

Kotlinでenumの序数を定義するとき、脳死でordinalを使うのは危険

2025/01/04に公開

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

まとめ

基本的にordinalnameは活用すべき。けどenumの列挙順に意味を持つのであれば、ordinalとは別の値を持った方がよい。

脚注
  1. JavaもEnumはordinal()メソッドを提供しているが、「EnumSetやEnumMapなどの高度なenumベースのデータ構造で使用するために設計されている」とあるため実質非推奨となっているっぽい。https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Enum.html#ordinal-- ↩︎

Discussion