enumとは
列挙型。列挙クラス。
内部的には、java.lang.Enumクラスを継承したクラス。1
なお、enumについては書籍『Effective Java』の第6章が詳しい。
著者のJoshua BlochはJSR 201のスペックリードである。
enumが解決する問題
enum以前
当初、区別することのみを目的とした定数宣言はint enumパターン2で記述されていた。
public final static int JANUARY = 0;
public final static int FEBRUARY = 1;
public final static int MARCH = 2;
public final static int APRIL = 3;
public final static int MAY = 4;
public final static int JUNE = 5;
public final static int JULY = 6;
public final static int AUGUST = 7;
public final static int SEPTEMBER = 8;
public final static int OCTOBER = 9;
public final static int NOVEMBER = 10;
public final static int DECEMBER = 11;
public final static int UNDECIMBER = 12;
int enumパターンは、マジックナンバーを出現させないことにおいて有効だったが、以下のような問題があった。
1)型保証なし
intであるため、必要な場所で無関係なint値を渡したり、2つの定数を足し合わせるなど意味のない記述ができる。
2)名前空間がない
定数に文字列を接頭辞として追加し、その他のint列挙型と競合しないようにしなければならない。
3)脆弱性
int列挙はコンパイル時定数である。新規定数が既存の2定数間に追加されたり、順番が変更になると、クライアントを再コンパイルしなければならない。コンパイルしないと、実行は可能だが、動作は予測できない。
4)出力値に情報価値がない
出力はすべて数値になる。意味がわからず、その型が何であるかもわからない。
タイプセーフenumパターン
int enumパターンの問題を解決したのがタイプセーフenumパターンである。
列挙定数をひとつのSingletonクラスで提供する。
public class Month {
private final String name;
private final int ordinal;
private Month(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String name() {
return name;
}
public int getValue()
{
return ordinal() + 1;
}
public String toString() {
return name;
}
public static final Month JANUARY = new Month("JANUARY", 0);
public static final Month FEBRUARY = new Month("FEBRUARY", 1);
public static final Month MARCH = new Month("MARCH", 2);
public static final Month APRIL = new Month("APRIL", 3);
public static final Month MAY = new Month("MAY", 4);
public static final Month JUNE = new Month("JUNE", 5);
public static final Month JULY = new Month("JULY", 6);
public static final Month AUGUST = new Month("AUGUST", 7);
public static final Month SEPTEMBER = new Month("SEPTEMBER", 8);
public static final Month OCTOBER = new Month("OCTOBER", 9);
public static final Month NOVEMBER = new Month("NOVEMBER", 10);
public static final Month DECEMBER = new Month("DECEMBER", 11);
}
これがenumが登場する以前(Java5以前)の標準的な記述方法である。
enum
Java5以降ではenumクラス(列挙型)を使用することができる。
public enum Month {
JANUARY,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER;
public int getValue() {
return ordinal() + 1;
}
}
簡潔な記述だが、原理的にはタイプセーフenumパターンを採用し、拡張させたものである。
タイプセーフenumパターンが適用されていることは、enumクラスをコンパイルしたclassファイルを逆コンパイルすることで確認できる。
逆コンパイル結果を以下に示す。
public final class Month extends Enum
{
private Month(String s, int i)
{
super(s, i);
}
public int getValue()
{
return ordinal() + 1;
}
public static Month[] values()
{
Month amonth[];
int i;
Month amonth1[];
System.arraycopy(amonth = ENUM$VALUES, 0, amonth1 = new Month[i = amonth.length], 0, i);
return amonth1;
}
public static Month valueOf(String s)
{
return (Month)Enum.valueOf(g06_enum/Month, s);
}
public static final Month JANUARY;
public static final Month FEBRUARY;
public static final Month MARCH;
public static final Month APRIL;
public static final Month MAY;
public static final Month JUNE;
public static final Month JULY;
public static final Month AUGUST;
public static final Month SEPTEMBER;
public static final Month OCTOBER;
public static final Month NOVEMBER;
public static final Month DECEMBER;
private static final Month ENUM$VALUES[];
static
{
JANUARY = new Month("JANUARY", 0);
FEBRUARY = new Month("FEBRUARY", 1);
MARCH = new Month("MARCH", 2);
APRIL = new Month("APRIL", 3);
MAY = new Month("MAY", 4);
JUNE = new Month("JUNE", 5);
JULY = new Month("JULY", 6);
AUGUST = new Month("AUGUST", 7);
SEPTEMBER = new Month("SEPTEMBER", 8);
OCTOBER = new Month("OCTOBER", 9);
NOVEMBER = new Month("NOVEMBER", 10);
DECEMBER = new Month("DECEMBER", 11);
ENUM$VALUES = (new Month[] {
JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER,
NOVEMBER, DECEMBER
});
}
}
タイプセーフenumパターンを適用し、Enumクラスを継承したSingletonクラスである。
valuesおよびvalueOfメソッド、ENUM$VALUESなどはコンパイラにより自動生成される。3
enumでできること
引数ありコンストラクタの定義
public enum Month {
JANUARY(1),
FEBRUARY(2),
MARCH(3),
private int monthNumber;
private Month(int param) {
monthNumber = param;
}
}
以下のようにコンパイルされる。
public final class Month extends Enum
{
private Month(String s, int i, int param)
{
super(s, i);
monthNumber = param;
}
public static Month[] values(){/*省略*/}
public static Month valueOf(String s){/*省略*/}
public static final Month JANUARY;
public static final Month FEBRUARY;
public static final Month MARCH;
private int monthNumber;
private static final Month ENUM$VALUES[];
static
{
JANUARY = new Month("JANUARY", 0, 1);
FEBRUARY = new Month("FEBRUARY", 1, 2);
MARCH = new Month("MARCH", 2, 3);
ENUM$VALUES = (new Month[] {
JANUARY, FEBRUARY, MARCH
});
}
}
Enumクラスの利用
java.lang.Enumクラスと継承関係にあるため、EnumのAPIを利用できる。