Java の enum って何者なのかを整理する

著者
Kato83
作成日
2020/07/10 - 18:32
更新日
2023/03/02 - 13:40

新卒エンジニアとして働き始めて約2年半、遂にenumを用いた実装をすることとなったので、頭の中の整理を兼ねてまとめてみる。

~~むしろよく今までenumを避けてこれたなと思う~~

注意

以下、間違ったこと言っている可能性もあるので、鵜呑みにしないように。
マサカリまってます。

enum is 何

よく列挙型と言われるが、要は識別子をまとめたものと言える。
文字列や数値だと判別しにくかったり、ヒューマンエラーを起こしやすいものを特定の型を実装して、見た目で判別しやすいようにするためのもの、と言っていいのかもしれない。

なお、記事タイトルは Javaのenum と書いているが、他の言語でも同じことが言えるはずです。

public final static Integer MAX_SIZE = 100;

Javaにてこのような実装をする機会があると思う。
100という値が何者なのかわかるように、使い回しできるように、このような変数を定義している。
※例として、今回の場合は最大サイズと識別したいので MAX_SIZE という変数名にしている。

このような変数が1つだけであればいいのだが、複数定義したいのであればenumを使うことを検討すると良さげ。

public enum Paging{
  MAX_SIZE(100),
  MIN_SIZE(10),
  DEFAULT_SIZE(50);

  Integer size;
  Paging(Integer size){
    this.size = size;
  }

  public Integer getValue(){
    return size;
  }
}
```sun mon tue wed thu fri sat

他には、曜日などもenumを用いる良い例かもしれない。  
人によっては月曜日を `月` とプログラム上に書かれたり、 `sunday` と書く人、日曜日を0として `1` と書く人もいるだろう。  
そのような実装の揺れを減らす目的でも用いられる。

```java
public enum WeekDay{
  SUN, MON, TUE, WED, THU, FRI, SAT;
}

enumを用いた実装例

引数に応じた言語での「こんにちは」を返却する実装をenumあり無しの2パターン作成してみる。

enum無しの実装

以下は言語の判定を国の2文字コード(文字列)を引数として判定している。

public static String multiLangHello(String lang) throws Exception {
    // nullチェック
    Objects.requireNonNull(lang);
    // emptyチェック
    if (lang.isEmpty()) {
        throw new Exception("言語が指定されていません");
    }
    switch (lang) {
        case "ja":
            return "こんにちは";
        case "en":
            return "Hello";
        case "de":
            return "Guten tag";
        case "pe":
            return "Olá";
        default:
            throw new Exception("指定した言語は対応していません");
    }
}

この実装でも問題ないが、条件分岐用の引数 lang が文字列であれば何でも指定できる点にある。
例えば、この実装では日本語の挨拶を出力したい場合、 ja を引数に指定する必要があるが、 jpJA (大文字)、 japanese という引数が渡されてしまう可能性もある。

Switchのcaseを増やせば対応できなくはないが、そのような対応をし始めるとキリがなく、以下のような残念な実装となってしまう。

lang = lang.toLowerCase();
switch (lang) {
    case "ja":
    case "jp":
    case "japanese":
    case "にほんご"
    case "日本語"
    case "🇯🇵"
    case "ja_jp"
    case "やさしい日本語"
        return "こんにちは";
// 以下省略

「やさしい日本語」について | 2020年オリンピック・パラリンピック大会に向けた 多言語対応協議会ポータルサイト

このような冗長な実装となりうる処理をenumを用いて実装すると以下のようにできる。

enum Lang{
    JA, EN, DE, PE, IT
}

public static String multiLangHello(Lang lang) throws Exception {
    // nullチェック
    Objects.requireNonNull(lang);
    switch (lang) {
        case JA:
            return "こんにちは";
        case EN:
            return "Hello";
        case DE:
            return "Guten tag";
        case PE:
            return "Olá";
        default:
            throw new Exception("指定した言語は対応していません");
    }
}

これで引数で渡ってくる値を Lang enumで列挙している JA, EN, DE, PE, IT に絞ることが出来る。

万が一、文字列を指定されたり Lang enumで列挙していない値を渡そうとしても、Javaビルドする際にエラーを表示してくれ、ヒューマンエラーを防げる。

これが enum の良さと言えることを今回学んだ。

最後に、検証で使用したソースコードを全部載せておきます。
お手元の環境で実際に動作させてみるとより理解が深まるかもしれない。

ソースコード全体

import java.util.Objects;

public class EntoryPoint {
    public static void main(String[] args) {
        try {
            System.out.println("multiLangHello(\"ja\") = " + multiLangHello("ja"));
            System.out.println("multiLangHello(Lang.JA) = " + multiLangHello(Lang.JA));
            // 未定義なのでエラーになる
            // System.out.println("multiLangHello(Lang.NZ) = " + multiLangHello(Lang.NZ));
            //  System.out.println("multiLangHello(\"japan\") = " + multiLangHello("japan"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String multiLangHello(String lang) throws Exception {
        // nullチェック
        Objects.requireNonNull(lang);
        // emptyチェック
        if (lang.isEmpty()) {
            throw new Exception("言語が指定されていません");
        }
        switch (lang) {
            case "ja":
                return "こんにちは";
            case "en":
                return "Hello";
            case "de":
                return "Guten tag";
            case "pe":
                return "Olá";
            default:
                throw new Exception("指定した言語は対応していません");
        }
    }

    enum Lang{
        JA, EN, DE, PE
    }

    public static String multiLangHello(Lang lang) throws Exception {
        // nullチェック
        Objects.requireNonNull(lang);
        switch (lang) {
            case JA:
                return "こんにちは";
            case EN:
                return "Hello";
            case DE:
                return "Guten tag";
            case PE:
                return "Olá";
            default:
                throw new Exception("指定した言語は対応していません");
        }
    }

}

それでは。

Category