Java
列挙型

列挙型について考察

恥ずかしながら今まで仕事でプログラムを書いていて列挙型というものを使ったことがない。(業務上のソースで見たこともない。)

列挙型の使い勝手を調べてみてよさそうなら今後チャンスを見計らって列挙型を使ってみようと思う。

とりあえず書いてみる

例えば下記のようなステータスを定数で管理している場合

定数クラス
public static int STATUS_STOPED = 0;  //停止済み
public static int STATUS_STARTING = 1;//起動中
public static int STATUS_STARTED = 2; //起動済
public static int STATUS_STOPPING = 3;//停止中
public static int STATUS_ERROR = 9;   //エラー発生

列挙型で書き換えるとこんな感じになる

列挙型
    public enum Status {
        STOPED,   // 停止済
        STARTING, // 起動中
        STARTED,  // 起動済
        STOPPING, // 停止中
        ERROR     // エラー
    };
定数
「STATUS_STOPED」という定数は「0」という値を格納していてそれは「停止済」を表す。 と3つの情報を紐づける必要がある。
列挙型
「STOPED」という値は「停止中」を表す。 と2つの情報を紐づけるだけでいい。

この紐づけを間違ってしまうと大変なことになるので紐づけが単純って意味で使いやすいかもしれないですね。

DBに格納してみる

では上記のステータスをDBに格納するとなればどうだろう?
DBでステータスを保持する際に文字型で格納するイメージはないので「ステータス」という整数型のカラムを用意して格納するとする。


定数型

int型で定義しているのでそのまま格納すればOKでしょう。

列挙型

当然、そのままはDBに格納できない。
数値型に変換する必要がある。数値が定義された順で連番で良ければ下記のコードでOK。
        System.out.println(Status.STOPED.ordinal());    // 0
        System.out.println(Status.STARTING.ordinal());  // 1
        System.out.println(Status.STARTED.ordinal());   // 2
        System.out.println(Status.STOPPING.ordinal());  // 3

連番ではなくて決まっている数値(エラーは9にしなければならない)という制約があるならこうする必要がある。

列挙型
    public enum Status {
        STOPED(0),
        STARTING(1),
        STARTED(2),
        STOPPINMG(3),
        ERROR(9);

        int id; // 各ステータスを数値で表現
        Status(int id){
            this.id = id;
        }

        public int getId() {
            return this.id;
        }
    }

こんな風に定義して呼び出し側では

呼び出し側
Status.STOPED.getId();

こうすれば数値として扱えるようになる。
・・・結局「STOPED」は「0」を表していて「停止中」を意味するって3つの情報が出てきました。
しかも数値で扱えるようにするためだけにフィールド、コンストラクタ、フィールドのゲッターの3つを用意する必要がある。
ここまでするのならそれに見合うメリットがないとちょっと使いずらいですね。。。


メソッドの引数として渡してみる

上記のステータスを引数として受け取るメソッドを考えてみる


定数
引数が定数
    /**
     * 引数のステータスの値に応じて処理を行う
     * @param status 停止済(0)or起動中(1)or起動済(2)or停止中(3)orエラー(9)<br>
     *               上記以外の数値が渡されるとIllegalArgumentExceptionを発生させる
     */
    private void doProcess(int status) {

        List<Integer> statusList = Arrays.asList(0,1,2,3,9);

        // ステータスが不正
        if(!statusList.contains(status)){
            throw new IllegalArgumentException("ステータスが許容値の範囲外です。(status:"+status+")");
        }
        // do something
    }
}
ステータスを表す値としては停止済(0)、起動中(1)、起動済(2)、停止中(3)、エラー(9)の5通りしか意味を持たないが、定数の場合はint型なので5や-1などほかの数値を渡してもエラーにならない。(が、これらの数値が渡ってくると動作が保証できなくなる。)よってメソッドの中で0,1,2,3,9以外の値が渡されたら例外を返すなどの入力チェックが必要となる。 そのことを利用者にも伝えなければいけないのでjavadocに書くことも増える。


列挙型
引数が列挙型
   /**
     * 引数のステータスの値に応じて処理を行う
     * 
     * @param status
     */
    private void doProcess(Status status){

        // do something 
    }

メソッドに渡せる値の範囲が制限されるので入力チェックが不要になり、コメントにもアレコレ注意事項を書く必要がありません。
列挙型を使うと宣言部が少し複雑になりますがメソッド内が完結になるのはいいですね。

まとめ

  • メソッドの引数に使わないかつ、DBに連番ではない整数型で格納する必要があるなら定数

  • それ以外なら列挙型

という使い分けがいいのかなと思いました。
すべてのケースで列挙型でもいいと思いますが1番目くらいのユースケースなら手軽な方でいいのでは?という感想です。

仕事でも使えるケースがありそうなので取り入れてみたいと思います!