【Java】列挙型(Enum)について

スポンサーリンク

列挙型(Enum)についてまとめました。Enumerated Typeの略だそうです。

enum宣言について

enum宣言の文法は以下の通りです。

[修飾子] enum enum名 [implements インターフェース名] { enum定数, enum定数, ...[,;] }

トップレベルで宣言する場合の文法はクラス宣言とほぼ同じで、public の付いた enum名はソースファイル名と同じでなければならず、また、public の付いた enum はソースファイル内に1つしか定義できません。クラス宣言との違いは、必ず先頭に enum定数の列挙が必要な点です。

enum型をトップレベルで宣言する場合(Gender.javaファイルで宣言する)

public enum Gender { MAN, WOMAN, OTHER }

enum型をクラス内で宣言する場合(Hoge.javaファイルで宣言する)

public class Hoge {
    enum Gender { MAN, WOMAN, OTHER }
}

enum宣言で定義した型はjava.lang.Enumクラスの継承型となり内部的にはクラスですが、通常のクラスと異なり new 演算子でオブジェクトを生成できません。そのため、enum定数が暗黙にオブジェクトを生成するので、MANWOMANOTHERのenum型オブジェクト生成されることになります。

emun型オブジェクトは単一インスタンス(シングルトン)が保証されているので==演算子で同一性比較が可能です。eqqualsも使えますが内部では==を使用しているのでどちらも同じです。

enum定数の値

enum定数はフィールド名の定数(クラス内のstatic final変数)と同様であり、アルファベットの大文字を使うのが通例となっています。また、区別するだけの enum 定数もあれば、意味のある enum 定数もあるので、以下のように値を取得することが可能です。

Gender.MAN.name();
//=> "MAN"という文字列を返す。オーバーライドできない。
Gender.MAN.toString();
//=> デフォルトは name() と同じ。オーバーライド可能。
Gender.OTHER.ordinal();
//=> enum定数の列挙順(0から開始)を返す。この場合は2。オーバーライドできない。

enum型の振る舞いについて

内部的には特殊なクラスであるため、任意のコンストラクタ、フィールド、メソッドを追加して振る舞いを持たせることもできます。

enum型のオブジェクトに特定の値を持たせたい場合は以下のように宣言します。

public enum Gender {
    // コンストラクタの呼び出し
    MAN(0),
    WOMAN(1),
    OTHER(2);  // 振る舞いを持たせる場合はセミコロンが終端

    private final int id;  // メンバ変数

    Gender(int id) {  // privateコンストラクタ
        this.id = id;
    }
}

決まり事として、コンストラクタは必ず private でなければいけません。ただし、enum型のコンストラクタは暗黙に private なので、private 修飾子は省略可能です。コンストラクタの呼び出しは enum定数の宣言のところで行ないます。

任意のメンバ変数を追加可能ですが、アクセス修飾子は通常のクラスと同じ意味です。つまり public であれば直接アクセスすることが可能ですが、通常は private にして getter を実装します。

以下のようにメソッドを追加することでメソッドの呼び出しも可能になります。

public int getId() {
    return id;
}
...
// 引数に列挙型オブジェクトを受け取る
int genderId = getGnenderId(Gender.MAN);
public int getGenderId(Gender num) {
    return num.getId();
}

初めて enum に触れる場合は、この辺りが中々理解できないところかもしれませんが、イメージとしては以下のように、暗黙的に Gender クラスのオブジェクトが生成されるというイメージです。逆に分かりづらくなったらすみません。

public static final Gender MAN = new Gender(0);
public static final Gender WOMAN = new Gender(1);
public static final Gender OTHER = new Gender(2);

enum定数の列挙

Enum型の static メソッドであるvalues()メソッドで enum 値を列挙できます。values()メソッドは enum 値の記述順序(コードに書いた順)を保証します。

for (Gender gender : Gender.values()) {
    gender.exec();
}

enum定数に特定の値(ID等)を持たせている場合、それらの値から enum オブジェクトに変換することはできません。このような場合は以下のようなメソッドを実装しておくことによって、enum に変換することが可能です。

    public static Gender valueOf(int id) {
        for (Gender gender : Gender.values()) {
            if (gender.getId() == id) {
                return gender;
            }
        }
        throw new IllegalArgumentException();
    }
...
Gender gender = Gender.valueOf(1);  // IDからenumに変換  

String から enum への変換はデフォルトで可能です。

Gender gender = Gender.valueOf("WOMAN");  // enum定数の文字列から変換

インターフェースと抽象メソッド

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

public enum Gender implements HogeInterface {
    MAN(0),
    WOMAN(1),
    OTHER(2);
    ...
}

抽象メソッドを宣言した場合、enum定数の宣言のところで実装しなければいけません。

public enum Gender {
    MAN(0) {
        @Override
        public void exec() {

        }
    },
    ...
    public abstract void exec();
}

enum定数とswitch文

enum型はswitch-case文に使えます。ただし、enum定数をそのまま書いた場合は問題ありませんが、以下のようにメンバ変数の値を使用することはできません。メンバ変数はオブジェクトが生成されて初めて使用できるものなので、case式に定数以外を指定すると「定数式が必要です」とコンパイルエラーになってしまいます。

switch (id) {
    case EnumSample.MAN.getId():
    case EnumSample.MAN.getId():
    case EnumSample.MAN.getId():
    default:
}