enumの使い方 (含: C言語との比較)

目次

1  Javaの列挙型

初期のJavaでは、何らかの値のグループを表す構文がなく、static final int による定数で、グループの中の個々の値を定義していた。 この方法は、グループのメンバーが追加・削除などの変更がある場合に気をつけるべき変更点が多く、保守性がよくない。
そこで Java 5 以降では、何らかの値のグループを表す構文として enum が導入された。 元々 enum はC言語由来のものだが、C言語のenumが整数定数値に名前を付けるものであるのに対し、Javaのenumは以下の特徴を持つ。

  1. enum 自体が特別扱いのクラスである (enum E は java.lang.Enum<E> のサブクラスとなる)

    new や clone() 等で新たなインスタンスを作ることができない。
    コンストラクタはprivate
    クラス内部で定義されたenumは暗黙でstatic扱い。明示的に static を記してもよい。
  2. enum の各 列挙子 (enum定数、列挙定数) は、そのenumクラスのインスタンス (定数オブジェクト) であり、final なクラス変数 (static変数) としてアクセスできる。

最も基本的な使い方は、見かけ上はC言語のenumと似ている。

enum   クラス名    {  列挙子の並び(コンマ区切り)   }

ただし、C言語と違い、各列挙子は整数でなくオブジェクトで、= で値を設定することはできない。

(例)

enum Suit {
	SPADES,
	HEARTS,
	DIAMONDS,
	CLUBS
}

上の例では、列挙子 SPADES, HEARTS, DIAMONDS, CLUBS を持つ列挙型(クラス) Suit を定義している。

実際には、上記のenumに対して下記のようなクラスが定義されていると見なしてよい。
※ 厳密ではないので、完全に下記と同一になるわけではない。

final class Suit extends Enum<Suit> {
	private Suit(String name, int ordinal) {
		super(name, ordinal);
	}

	public static final Suit SPADES = new Suit("SPADES", 0);
	public static final Suit HEARTS = new Suit("HEARTS", 1);
	public static final Suit DIAMONDS = new Suit("DIAMONDS", 2);
	public static final Suit CLUBS = new Suit("CLUBS", 3);


	・・・・・・

}

上記「見なしコード」でも分かるが、enumを利用する際は、列挙子をクラスのstatic変数として参照できる。すなわち、「クラス名.列挙子名」という形で利用できる。

Suit s = Suit.SPADES;

この例では、列挙型(クラス) Suit を表す変数 s を列挙子 SPADES の値に設定している。
また、switch文でenumの値による分岐が利用できる。例えば、上記変数 s による多岐分岐を

switch ( s ) {
case SPADES:
	// スペードのときの処理
	break;

case HEARTS:
	// ハートのときの処理
	break;

......

}

のように記述できる。caseラベルでは、enumのクラス指定をせず、単に列挙子名だけを指定しなければならないことに注意。
また、上記コードで s の値が null だと、switch文において NullPointerException が発生するので注意しなければならない。

「enum クラス名」を定義すると、以下の static メソッドが自動で生成され、「クラス名.メソッド( 引数 )」の形で利用できる。

public static クラス名[]  values()
	全列挙子を宣言順に並べた配列を返す。

public static クラス名  valueOf(String name)
	name で指定した列挙子オブジェクトを返す。
	name が null の場合は NullPointerException が発生する。
	指定された name の列挙子がない場合は IllegalArgumentException が発生する。

enum定数 (列挙子) オブジェクトでは、いくつかのインスタンスメソッドが利用できる。主要なものを以下に示す。

public final String name()
	列挙子の名前を返す。

public final int ordinal()
	列挙子の序数 (0 から始めて1ずつ増加する番号) を返す。

public String toString()
	列挙子の文字列表現を返す。
	デフォルトのものは、name() と同じく、列挙子の名前を返す。
	オーバーライドして独自文字列を返すように変更できる。

より進んだenumの使い方として、enumはクラスなので、中で、クラス本体として変数・コンストラクタ・メソッドを定義できる。
列挙部分とクラス本体の区切りとしてセミコロンを入れる必要がある。

enum   クラス名    {
		列挙子の並び(コンマ区切り)  ;      // 最後に ; (セミコロン) が必要


		クラス本体
		(変数・コンストラクタ・メソッドの定義)

}

列挙子に特別な値を設定したいときは、コンストラクタを定義して、コンストラクタにパラメータを渡しインスタンス変数に保存すればよい。

コンストラクタのパラメータの個数は任意だが、2個の場合の記述法を以下に示す。

enum   クラス名    {
		列挙子1(引数1, 引数2),
		列挙子2(引数1, 引数2),
		列挙子3(引数1, 引数2),
		... // 列挙を続ける

		;      // 列挙の終りに区切りの ; (セミコロン) が必要

		// クラス本体

		private final 型1  変数名1; // インスタンス変数。final にしなければならないことはないが、
		private final 型2  変数名2; //   enumの性質上、final にして immutable (不変) なクラスとするべき。
						// 利用状況にもよるが、一般的には private も指定した方がよい。

		クラス名(型1  パラメータ1, 型2  パラメータ2) {  // コンストラクタ
				this.変数名1 = パラメータ1;
				this.変数名2 = パラメータ2;
				...
		}

		... // 他のメソッドの定義

}

さらに、個々の列挙子に対して、メソッドをオーバーライドすることもできる。
定数固有メソッド実装 (constant-specific method implementation) と言われる。

enum   クラス名    {
		列挙子1 {
			メソッドAの実装またはオーバーライド
		},
		列挙子2 {
			メソッドAの実装またはオーバーライド
		},
		...

		;

		// クラス本体
		
		メソッドAの定義 (抽象メソッドでもよい)
}

クラス本体で抽象メソッドを宣言した場合は、全ての列挙子において、定数固有メソッド実装によって、その抽象メソッドを実装しなければならない。

2  C言語の列挙体 (enum定数)

C言語では、列挙体 (enum定数) によって、整数値定数に名前をつけて使用することができる。

enum   タグ名    {  定数の並び(コンマ区切り)   };

または

enum   {  定数の並び(コンマ区切り)   };

という構文で、(複数の) 名前のついた定数を定義できる。これらの定数はあくまでも整数型として扱われる。

列挙体の値が入る変数も使用できる。

enum   {  定数の並び(コンマ区切り)   }    変数名;

あるいは

enum   タグ名    {  定数の並び(コンマ区切り)   }    変数名;

の形で、列挙体自身の定義と変数の定義をまとめてできる。
または、上部で

enum   タグ名    {  定数の並び(コンマ区切り)   };

を定義しておき、後方で、

enum   タグ名    変数名;

のようにして変数を定義できる。

(例1)

enum {
	LS_BASE,
	LS_FIRST,
	LS_NEXT = 101,
	LS_NEXT2,
	LS_LAST = 199,
};

= によって値を指定していない定数は、0 から順に 1 ずつ増加していく値に設定される。
= で値を指定してある定数は = の右側の値に設定される。
その後の定数は、直前に指定された値から順に 1 ずつ増加していく値となる。

上の例では、

LS_BASE,
LS_FIRST,
LS_NEXT,
LS_NEXT2,
LS_LAST

は、順に

0, 1, 101, 102, 199

に設定される。

(例2)

enum Suit {
	SPADES,
	HEARTS,
	DIAMONDS,
	CLUBS
} suit = SPADES;

上の例では、列挙体 Suit を表す変数 suit (値 SPADES を持つ) が定義されている。
定数

SPADES,
HEARTS,
DIAMONDS,
CLUBS

は、順に

0, 1, 2, 3

に設定される。