【Java】enum(列挙子)の持つメソッド、基本的な使い方、応用(シングルトン)【実装例付き】

前書き:君、C言語のenumと雰囲気違うね

仕事でJavaのコードを読んでいる時、「Javaのenumは、C言語のenumより多機能だな」と感じる場面がありました。

Javaのenumを多機能と感じた例として、以下の3点が挙げられます。

  • enumにメソッドがある(メソッドを実装できる)
  • 複数のフィールド(変数)を持てる
  • シングルトンを作成する際にenumを使用する

他の言語と比較しても多機能なJavaのenumに関して、本記事では基本的な使い方から応用(シングルトンとして利用する方法)まで順番に説明していきます。

               

enumが持つメソッド一覧

Java SE11のjava.baseモジュールのjava.lang.Enumには、下表のメソッドが定義されています。例外を投げるclone()、実装できないfinalize()を除いて、下表のメソッドの実装例/実行結果を順に示します。

No. メソッド名 返り値の型 説明
1 clone() Object Clone不可。CloneNotSupportedExceptionをスロー。
2 compareTo(E o) int enum定数の順序を比較
3 equals(Object other) boolean enum定数が同じかどうかをチェック
4 finalize() void finalizeメソッドを持てない
5 getDeclaringClass() Class<E> enum定数のenum型に対応するClassオブジェクトを返す
6 hashCode() int enum定数のハッシュコードを返す
7 name() String enum定数の名前を返す
8 ordinal() int enum定数の序数(番号)を返す
9 toString() String enum定数の名前を返す
10 valueOf(Class<T> enumType, String name) Enum<T> 指定された名前のenum定数を返す

                   

compareTo()メソッド 

enum定数の序列(番号)の差を結果として返しますが、直感的ではないので使いづらい印象です。

                  

getDeclaringClass() 

enum定数が定義されているファイル名とenum名を返します。

                     

equals()メソッド 

equals()メソッドを使用しなくても、”==”で比較できます。

                     

hashCode()メソッド 

enum定数のハッシュ値を算出します。

ハッシュ値は、ハッシュテーブル探索(HashMap、HashSet)で使用されるため、以下の条件を満たして実装する必要があります(enumの場合は、デフォルトで条件を満たしています)

  • equals() の結果で true を返すオブジェクトは、同じハッシュ値である事
  • ハッシュ値が異なる場合は、equals() の結果で false を返す事
  • equals() の結果でfalse を返すオブジェクト(複数)が、同じハッシュ値を返しても良い

              

name()メソッド 

enum定数の定義名称を返します。

ユーザフレンドリーな名称を返すには、toString()メソッドを使用した方が良いとAPIドキュメントに書かれています。

つまり、ユーザフレンドリーな文字列を返すようにtoString()メソッドをオーバライドし、name()メソッドの使用を控えた方が好ましいという事です。toString()をオーバライドする例は、toString()メソッドの実装例として後述します。

                    

ordinal()メソッド 

enum定数の序数(番号)を返します。序数は、0オリジンであり、enum定数が増えるとインクリメントされる仕様です。

                

toString()メソッド 

toString()メソッドは、オーバライドしていなければname()と同じ結果を返します。

以下の例では、toString()メソッドをオーバライドし、name()メソッドとは異なる文字列を返します。今までの実装と異なる箇所は、以下の3点です。

  • enum定数ごとに文字列(変数name)を持つ
  • toString()は、変数nameをそのまま返す
  • コンストラクタの公開範囲はprivate

               

values()メソッド 

enum定数の名称を全て返します。

                  

C言語ライクな使い方の例

Javaのenumの最も単純な使い方の例として、enum Actor(役者)を定義して、whoメソッドの引数がどの役者であったかを判定するプログラムを以下に示します。

enum Actor {}部分の実装は、C言語であれば連番を1から順に割り当てている状態です。

enum定数を指定(例:Actor.LUCIE)して、whoメソッドにActorクラスとして引数渡しできます。この使い方は、C言語と比較して違和感がありません。

実行結果を以下に示します。

                    

enum定数が数値と文字列を持つ例(C言語では不可)

C言語ではenum定数に複数の値を持たせられませんが、Javaでは複数の変数を持たせられます。変数の内容を渡すgetterメソッドを追加する事もできます。

複数の変数を持てるので、「国名や国コード(例:日本, +81)」、「バージョンとバージョン名称(11, Bullseye)」など、組み合わせが決まっているものをenumで表現しやすくなります。

以下の例では、曜日とIDを変数として持つenumの例です。各変数には、getterメソッドを追加しています。

実行結果は、以下の通りです。

                

応用:enumでシングルトンを作成

Javaの言語仕様上、Enumがグローバルに唯一のインスタンスとなる事が保証されます。Enumは、厳密なSingletonであり、スレッドセーフかつabstractな実装もできます。

シングルトンを作成する方法に関して、以下の記事で紹介しています。

【Singeltonパターン】考え方は単純だが、使いどころが大切なデザインパターン【コード例はRubyとJava】

              

 後書き

Javaのenumは、拡張性が高すぎる印象です。使い方の方針を予め考えておかないと、過剰設計なプログラムを産み出しそうです。

               

おすすめ