【Java】列挙型についてメモ

はじめに

Enum型について個人的なメモを記載する。

Enum(シンプルな形式)

Enumの(多分)最もシンプルな形式は以下の通り。このEnum定数を使う側は、「MyEnum.RED」のように記述する。

public enum MyEnum {
	RED, BLUE, GREEN,
}

これはリフレクションの概念も絡んでくることではあるが(自分はまだリフレクションについての理解が十分ではないが)、このとき、以下のように記載すると、そのbooleanはtrueとなる。これについては後述する。

System.out.println(MyEnum.RED.getClass()==MyEnum.RED.getDeclaringClass());  //trueと表示される。


EnumEnum定数ごとに独自の実装を持たせる)

上記Enumを拡張した形が以下の通り。各Enum定数ごとに、その定数に対応するColorオブジェクトを返すメソッド「getColor」を定義。
abstractメソッドgetColorを宣言し、その実装を各Enum定数ごとに実装(オーバーライド)する。

import java.awt.Color;

public enum MyEnum {
	RED {
		@Override
		Color getColor() {
			return Color.RED;
		}
	},
	BLUE {
		@Override
		Color getColor() {
			return Color.BLUE;
		}
	},
	GREEN {
		@Override
		Color getColor() {
			return Color.GREEN;
		}
	},
	;

	abstract Color getColor();
}

このとき、以下のように記載すると、そのbooleanはfalseとなる。これについては後述する。

System.out.println(MyEnum.RED.getClass()==MyEnum.RED.getDeclaringClass());  //falseと表示される。


getClassメソッドって何?getDeclaringClassメソッドって何?

メソッド 概要
Object #getClass() すべてのクラスの親であるObjectクラスで定義されているメソッド(=あらゆるクラスはgetClassメソッドを持っている)。実行時のクラスを返す。
Enum #getDeclaringClass() Enumクラスで定義されているメソッド。今回の場合、そのEnumm定数が宣言されているクラスを返す。

今回、2つのEnumの例を示したが、それぞれの場合のgetClassメソッドとgetDeclaringメソッドの実行結果をまとめると以下のようになる。

シンプルな形式 Enum定数ごとに独自の実装を持たせる
MyEnum.RED.getClass() class myProg.MyEnum class myProg.MyEnum$1
MyEnum.RED.getDeclaringClass() class myProg.MyEnum class myProg.MyEnum

「シンプルな形式」では、「MyEnum.RED.getClass()==MyEnum.RED.getDeclaringClass()」がtrueになると記述した。これは、それぞれのメソッドによって返ってくる値が同じだからである。

Enum定数ごとに独自の実装を持たせる」では、「MyEnum.RED.getClass()==MyEnum.RED.getDeclaringClass()」がfalseになると記述した。これは、それぞれのメソッドによって返ってくる値が違うからである。

じゃあ「$1」とは何か。これはコンパイラが作成した無名インナークラスを指す。
Enum定数ごとに独自の実装を持たせる」の実装は、以下と同じ意味である(REDにのみ注目。その他は割愛)。

public abstract class MyEnum {
        abstract Color getColor();

        public static final MyEnum RED = new MyEnum() {
                Color getColor() { return Color.RED; }
        }
        ・・・
}

・MyEnumクラス内でabstractメソッドを宣言しているため、MyEnumのインスタンスを作ることはできない。
・new MyEnum() {・・・}の部分は、クラス名を持たない無名なクラスで、かつ、getColorメソッドをオーバーライドしている。
・つまり、new MyEnum() {・・・}の部分は、「MyEnumクラスをextendsした無名のインナークラス」ということになる。
・総括すると、REDやBLUEといったようにenum定数の個々に固有の振る舞いを定義した場合、MyEnumクラスを継承したサブクラスのインスタンスコンパイラにより作成される。なおかつ、継承したサブクラスの名前は無い。


いろいろ言ったが要するに…

【シンプルな形式の場合】
・getClassで返ってくる値も、getDeclaringClassで返ってくる値も、ともに「class myProg.MyEnum」となる。
  - RED等が宣言されているのはMyEnum → getDeclaringClassの結果は「class myProg.MyEnum」
  - 定数ごとの実装が無い → getClassの結果は「class myProg.MyEnum」

【定数ごとに実装した場合】
・getClassで返ってくる値(=実行時のクラス)はMyEnumを継承した無名のクラスであり、その形式は「class myProg.MyEnum$1」となる。
・getDeclaringClassで返ってくる値は、その定数値が宣言されているクラスが返ってくる。今回の例の場合、「class myProg.MyEnum」となる。

おわりに

Enum型とgetClass、getDeclaringClassについて記述した。ミソは「定数ごとの実装があるかどうか」「実装がある場合、そのクラスを継承した無名のインナークラスがあること」ということになるのかな。結構ややこしい・・・。

参照

4. enum (2) | TECHSCORE(テックスコア)
Enum (Java Platform SE 6)
10-4. Classクラス - マンガで分かる Java入門講座 - マンガPG
Object (Java Platform SE 6)
四種類の内部クラス - にょきにょきブログ