Javaではint定数の代わりにenumを使用する

最近はEffective Javaをずっと読んでいる。Javaでは定数宣言にpublic static final intを使わず、enumを使えとのことだったのでメモしておく。
 

従来の定数宣言

従来は、Javaで定数宣言をするときは、以下のように宣言していた。

//色の黒と白
public static final int COLOR_BLACK = 0;
public static final int COLOR_WHITE = 1;

//幼女向けアニメ「プリキュア」のブラックとホワイト
public static final int PRECURE_BLACK = 0;
public static final int PRECURE_WHITE = 1;

 
この方法は、int enumパターンとして知られており、以下のような多くの欠点を持っている。

  • 名前空間を持たないので、変数名に接頭辞(COLOR・PRECURE)を多用することになり、冗長である。
  • コンパイル時に呼び出した場所へインライン展開されてしまう(例:Integer apiVersion = Api.VERSION;がコンパイルされると、Integer apiVersion = Integer.valueOf(10);になる)ため、再コンパイルの必要がある。
  • 定数をデバッガから表示しても見えるのは数字であり、あまり役に立つ情報ではない。
  • グループ内の定数を全てイテレートしたり、グループの大きさを得たりする信頼できる方法が提供されていない。

 
int enumパターンの代わりに、enum型を使用することでこれらの欠点を全て無くすことが出来る。
 

enum型とは

enum型は、あるカテゴリーに属した複数の定数をひとまとまりとして取り扱うための仕組みである。J2SE 5.0のenumは、CやC++のenumのように内部的に整数で定数を保持するものではなく、タイプセーフenumと呼ばれるパターンを実現したものになっている。
 

単純なenum

まず初めに一番単純なenumの定義方法を例示する。

public enum Color { RED, BLUE, YELLOW, BLACK, WHITE }

 
enumは内部的にはクラスである。enum 型を定義する場所はクラスを定義する場所と同じ。enum型もクラス同様パッケージに属する。基本的には1ファイルにひとつのenum型を定義するが、内部クラスのように他のクラスの内部にも定義することができる。アクセス権やアクセス方法もクラスの場合と同じである。
 
次のように記述することで、上記のColorクラスを利用することが出来る。

import define.common.Color;
public class EnumTest {
    public static void main(String[] args) {
        Color color = Color.WHITE;
        System.out.println("Color is " + color); //"Color is WHITE"を出力する。 
    }
}

 
上記のenumをjavapコマンドで逆コンパイルすると、以下のように表示される。

Compiled from "Color.java"
public class Color extends java.lang.Enum{
    public static final Color RED;
    public static final Color BLUE;
    public static final Color YELLOW;
    public static final Color BLACK;
    public static final Color WHITE;
    static {};
    public static final Color[] values();
    public static final Color valueOf(java.lang.String);
}

 
逆コンパイルしたColorクラスは以下のような特徴を持っている。

  • java.lang.Enumクラスを継承している。
  • 各メンバはpublic static finalな定数である。
  • valuesメソッドは宣言された定数全てを含む配列を返すメソッドである。
  • valueOfメソッドは文字列から定数インスタンスを取得するメソッドである。

 

enum型のクラスにカスタムメソッドを追加して更に便利にする

以下は、enum型のクラスにカスタムメソッドを追加する例である。

public enum Precure {
    BLACK("なぎさ"),
    WHITE("ほのか");

    private final String name;

    //コンストラクタ
    public Precure(String name) {
        this.name = name;
    }

    public String name() {
        return this.name;
    }
}

 
enum内の各定数はインスタンスなので、実行時には暗黙的にコンストラクタが呼び出される。このコンストラクタには引数を持たせることができ、定数ごとに異なる引数を渡すことができる。
 
上記のコードは次のように呼び出して使用することが出来る。

System.out.println(Precure.BLACK.name()); // 「なぎさ」と表示される。

 

定数ごとに異なる振る舞いを持たせる

以下のように記述することで、定数ごとに異なる振る舞いを設定することが出来る。

public enum Precure {
    BLACK() {
        public String apply(String s) {
            return "<span style=\"color:#000000\">" + s + "</span>";
        }
    },
    WHITE() {
        public String apply(String s) {
            return "<span style=\"color:#FFFFFF\">" + s + "</span>";
        }
    };

    //抽象メソッドで、それぞれの定数にapplyメソッドの実装を強制する
    abstract public String apply(String s);
}

 
上記のコードは次のように呼び出して使用することが出来る。

System.out.println(Precure.BLACK.apply("キュアブラック!")); // 「<span style=\"color:#FFFFFF\">キュアブラック!</span>」と表示される。

 
 
以上
 
 
参考
書籍 Effective Java
4. enum | TECHSCORE(テックスコア)
J2SE 5.0 Tiger 虎の穴 Typesafe Enum

Article written by

Comments are closed, but trackbacks and pingbacks are open.