Posted at

Enum で振る舞う

More than 5 years have passed since last update.


Java の列挙型

Java の列挙型は、C 言語の enum と異なり、単なる int の定数列挙ではなく、オブジェクトの列挙になるので、メソッドを宣言したり、メンバ変数を宣言したりと、振る舞いを持たせることが出来る。

内部的には、列挙したオブジェクトは定数として扱われるので、列挙の数だけオブジェクトが予め生成されることになる。

詳細は Effective Java を読もう!


振る舞う enum

通常の enum の宣言は以下のようになる。

public enum MyEnum {

HOGE,
FUGA;
}

これにメンバ変数を追加すると、

public enum MyEnum {

HOGE(1),
FUGA(2);

private final int id;

private MyEnum(final int id) {
this.id = id;
}
}

コンストラクタは必ず private でなければならず、それ以外のアクセス修飾子とするとコンパイルエラーとなる。

コンストラクタの呼び出しは列挙の宣言のところで行う。

さらにメソッドを追加して、

public enum MyEnum {

HOGE(1),
FUGA(2);

private final int id;

private MyEnum(final int id) {
this.id = id;
}

public int getId() {
return id;
}
}

これで、以下のようにメソッド呼び出しが行えるようになる。

int hogeId = MyEnum.HOGE.getId();

int fugaId = MyEnum.FUGA.getId();

メソッドの引数に列挙型オブジェクトを受けるようにしておけば

public void doSomething(MyEnum num) {

num.getId();
}

とか出来るので、switch 文で分岐して振る舞いを変えるようなコードがスッキリする。

enum もクラスの一種なので、インタフェースを実装したり、抽象メソッドを宣言したりできる(別のクラスの継承と、宣言した enum を別のクラスで継承することはできない)。

public enum MyEnum implements SomeInterface {

HOGE(1),
FUGA(2);

private final int id;

private MyEnum(final int id) {
this.id = id;
}

public int getId() {
return id;
}
}

抽象メソッドを宣言した場合、定数オブジェクトの宣言のところで実装を書く。

public enum MyEnum {

HOGE(1) {
@Override
public void foo() {

}
},
FUGA(2) {
@Override
public void foo() {

}
};

private final int id;

private MyEnum(final int id) {
this.id = id;
}

public int getId() {
return id;
}

public abstract void foo();
}

enum はもともと Comparable インタフェースと Serializable インタフェースを実装している。


列挙型のメソッド

enum 型には特別なメソッドが幾つか用意されている。

列挙した定数オブジェクトをすべて得るには、Enum#values()メソッドを使う。

public enum MyEnum {

HOGE(1),
FUGA(2);

private final int id;

private MyEnum(final int id) {
this.id = id;
}

// id から、定数オブジェクトを逆引きするメソッド
public static MyEnum valueOf(int id) {
// values() で、列挙したオブジェクトをすべて持つ配列が得られる
for (MyEnum num : values()) {
if (num.getId() == id) { // id が一致するものを探す
return num;
}
}

throw new IllegalArgumentException("no such enum object for the id: " + id);

// Null-Object パターンにしたがって、列挙に UNKNOWN みたいなのを入れておくのも良い
// return UNKNOWN;
}

public int getId() {
return id;
}
}

String から enum への変換は予め用意されている。

MyEnum hoge = MyEnum.valueOf("HOGE");

enum から String への変換もある。

Enum#toString()はオーバライドが可能で、自分の好きな文字列にすることが出来る。

Enum#name()はオーバライドができないようになっている。宣言した通りの名前が得られる。

String hoge1 = MyEnum.HOGE.toString();

String hoge2 = MyEnum.HOGE.name();

Enum#ordinal()で、宣言された順番も得ることが出来る。

宣言された順番をコンパイラが勝手に検出してコードにしてくれるので、当然のことながら、順番を入れ替えるとかえってくる数字も変わってしまうので、かえってくる数字そのものを何処か別のところで比較したり使ったりすると、せっかくの型安全性も台無しになるので、あんまり使わないでね。

int order = MyEnum.HOGE.ordinal();