menu
書いてる野郎
orebike@gmail.com
この仕組を利用すると非常に堅牢なシステムが作れるので積極的に設計に取り入れたほうがよい。
作り方
public enum Hoge{ A, B, C; }
使い方
if(a == Hoge.A){ }
みたいな。enumはswitchとかの比較でも使えるのだ。
単なる列挙体ではない。 Java の enum は基本的にクラスであり、enum の宣言ってのは、static なメンバをつくりその内部に自分自身を new すること。
public class Hoge{ public static final A = new Hoge(); public static final B = new Hoge(); public static final C = new Hoge(); private Hoge(){} }
多分内部的にはこんな感じになるんだな
自力のみで new できて、それを静的に保持しているということは・・・これはシングルトン実装である。
基本的にクラスなのでコンストラクタを作ることができる
public enum Hoge{ A("えー", "ee"), B("びー", "bii"), C("しー", "shii"); private String piyo; private String fuga; private Hoge(String piyo, String fuga){ this.piyo = piyo; this.fuga = fuga; } public String getPiyo(){ return this.piyo; } }
なので、内部にメンバを保持して、このようなことができる
System.out.println(Hoge.A.getPiyo()); //=> "えー"
基本的にクラスなのでメソッドを作ることができる
public enum Hoge{ A, B, C; public String piyo(){ return "ぴよ"; } }
つまりこのようによびだせる
String piyo = Hoge.A.piyo();
基本的にクラスなので各インスタンス共通の値を保持することができる
値を列挙して宣言した後(コンストラクタ処理の後)に、その各情報を使って静的メンバに値を保持したり、各値間に関係性をもたせたりしたい場合がある。 例えば、方角 Enum 東西南北 を作って、北の反対メンバに南を保持したいような場合である。
コンストラクタの時点では各値はできあがってないようで、コンストラクタで自身の Enum の値を使うことができない。
その時は、クラスの static ブロックを使うことでそれが実現できる。
Effective Javaにのってたので。 深い理由はわからんが文法上リフレクションに対しても堅牢なシングルトンをenumで作れるようだ。
ちらっと読んだだけだと理解できなかったので原理を・・・
特徴の通り、Javaの Enum は特殊クラスで、宣言時にメンバの作成と自分自身のインスタンスをクラス変数に格納する。
そこでEnumの数を1個にすれば、その時点で生成されるインスタンスは1個になるということを利用してシングルトンを実装するというのだ。
enumはインスタンスメソッドもインスタンスのメンバも持つことができるので、外部からメソッドしか使わないという制約のうちでは普通のクラスと同様に振舞う。つまり
public enum Hoge{ INSTANCE; //ここから下にシングルトンにしたいクラスの中身のようなものを書く private String hoge; public getHoge(){ return hoge } }
のように書けば動く
Java は演算子による比較が貧弱なので switch 文で非常に基本的な型しか使えなかった。 だがこの分岐に enum を使うことができるのだ。これも enum がシングルトンであるという性質に基づく。つまり同一が同値とみなせるということになる。
switch(hoge.getPiyo()){ case A: //処理 break; case B: //処理 break; default: break; }
のように書ける。
ここで便利なのが switch に渡す値から得られる Enum は自明なので case につけるラベルを Enum名からフルで書く必要がない。
enum はシングルトンなので同値チェックが同一チェックと同じになるので ==
演算子で比較できる
serializable 耐性。Java の Enum は上記のように「同一性」が特徴なのだが、シリアル化した場合、その同一が崩れるような気がする。 しかし、Enum は値のみがシリアル化されるようで Deserialize の時に使用先の環境で再現されるので、使用先の環境上で同一性に問題は出ないということになっているらしい。
Enum はその Enum ごとにメソッドのオーバーライドが可能になっていて、個別に細かく挙動が変えられる。
Enum の名前に続きブロックを作りその中でオーバーライドする。
public enum Hoge{ A{ @Override public String piyo(){ return "えー"; } }, B{ @Override public String piyo(){ return "びー"; } }, C; public String piyo(){ return ""; } }
コンストラクタと併用したい場合は、コンストラクタ呼び出し記述の後にブロックをつければよい
public enum Hoge{ A("aaaa"){ @Override public String piyo(){ return "えー"; } },
内部クラス(インナークラス)ならぬ インナー Enum も作ることができる
値が階層関係になっているような繋がりの深い値はこれで定義するとよいだろう
enum Hoge{ AAA(Piyo.CCC), // 内部のEnumの値をメンバに保持してみる BBB(Piyo.DDD); private Piyo piyo; private Hoge(Piyo piyo) { this.piyo = piyo; } enum Piyo{ CCC, DDD; } }
内部の値を外部から取り出したいなら。このように普通に内部にアクセスすれば取り出せる。
Hoge.Piyo a = Hoge.Piyo.CCC;
当然 private キーワードで内部に限定することもできる。
Java の列挙体は Enum クラスを継承した特殊クラスという位置づけなので、 Enum 自体はこのような型に格納することができる。
Class<? extends Enum<? extends Piyo>> a = Hoge.class;
この場合の Piyo は Hoge Enum に implements されたインタフェースを指す。
これができてしまえば後は 普通の Class に対するリフレクションと同様に操作できる。
何度も言うように、Java の Enum は基本的にクラスなのでインタフェース実装ができる。
なのでこのように宣言すると
public enum HOGE implements IFuga{ A, B, C; } public enum PIYO implements IFuga{ A, B, C; }
どちらの値も同じインタフェースで扱うことができるようになる。
Enum は型に値を練り込んでしまうとも取れるので、型と値を混同しがちになってしまうが、 型と値は Enum においても完全に別物で同じように扱うことはできない。
特に内部 Enum を使うことで、その見た目からツリー構造の値を構築できると錯覚してしまうが、そんなもの構築することはできない