2014年4月21日月曜日

Javaのenumの使い方。

Javaのenumと戯れてたらいつの間にかstaticブロックと戯れていました。

enumの勉強してたので、学んだことをまとめます。

サンプルとしてジャンケンプログラムを作成する場合を考えてみます。

enum Hand{
 //まずは定数の列挙
 ROCK("グー", 0),
 SCISSORS("チョキ", 1),
 PAPER("パー", 2);
 //フィールド
 private final int num;
 private final String name;
 //コンストラクタ
 Hand(String name, int num){
  this.name = name;
  this.num = num;
 }
 //メソッド
 public static Hand parseHandFromNum(int num){
  switch(num){
  case 0: return ROCK;
  case 1: return SCISSORS;
  case 2: return PAPER;
  default: return null;
  }
 }
}
System.out.println(Hand.ROCK);
System.out.println(Hand.ROCK.name);
System.out.println(Hand.ROCK.num);
とすると出力は
ROCK
グー
0
となります。
列挙型について調べてたら逆コンパイルしたら面白いとあったので、してみました。

static final class Hand extends Enum
    {

        public static Hand parseHandFromNum(int num)
        {
            switch(num)
            {
            case 0: // '\0'
                return ROCK;

            case 1: // '\001'
                return SCISSORS;

            case 2: // '\002'
                return PAPER;
            }
            return null;
        }

        public static Hand[] values()
        {
            Hand ahand[];
            int i;
            Hand ahand1[];
            System.arraycopy(ahand = ENUM$VALUES, 0, ahand1 = new Hand[i = ahand.length], 0, i);
            return ahand1;
        }

        public static Hand valueOf(String s)
        {
            return (Hand)Enum.valueOf(EnumSample$Hand, s);
        }

        public static final Hand ROCK;
        public static final Hand SCISSORS;
        public static final Hand PAPER;
        private final int num;
        private final String name;
        private static final Hand ENUM$VALUES[];

        static 
        {
            ROCK = new Hand("ROCK", 0, "\u30B0\u30FC", 0);
            SCISSORS = new Hand("SCISSORS", 1, "\u30C1\u30E7\u30AD", 1);
            PAPER = new Hand("PAPER", 2, "\u30D1\u30FC", 2);
            ENUM$VALUES = (new Hand[] {
                ROCK, SCISSORS, PAPER
            });
        }

        private Hand(String s, int i, String name, int num)
        {
            super(s, i);
            this.name = name;
            this.num = num;
        }
    }

結果はそのまんまクラス。

じゃ、クラス使ってやればよくない?と思うかもしれないけども自分はこのコードのここに注目した。
        public static final Hand ROCK;
        public static final Hand SCISSORS;
        public static final Hand PAPER;
        private final int num;
        private final String name;
        private static final Hand ENUM$VALUES[];

        static 
        {
            ROCK = new Hand("ROCK", 0, "\u30B0\u30FC", 0);
            SCISSORS = new Hand("SCISSORS", 1, "\u30C1\u30E7\u30AD", 1);
            PAPER = new Hand("PAPER", 2, "\u30D1\u30FC", 2);
            ENUM$VALUES = (new Hand[] {
                ROCK, SCISSORS, PAPER
            });
        }
これをクラス使って自分で実装する手間を省く程度に考えると、クラスかenumを使うかを自分で判断できるのかなと。

以下の記事が参考になりました。
列挙型を使う Java 5.0を使ってタイプセーフな方法で定数を表現する
Switch文の条件式にenumじゃなくてnullを入れてみた

(追記 2014/04/22)

 クラスと比較したenumの利点
 public static final int ROCK = 0;
 public static final int SCISSORS = 1;
 public static final int PAPER = 2;
 public static void showHand(int hand){
  switch (hand) {
  case ROCK:
   System.out.println("グー");
   break;
  default:
   break;
  }
 }
上記のようにクラスで実装を行うとshowHandを使う人はint型のものは全て引数に取れるということになります。 この場合showHand()内で0,1,2以外はエラー処理を行わないといけなくなりますよね。
しかし、引数showHand(Hand hand)のようなメソッドを定義するとHand型以外のものは渡せなくなります。 そういう意味でenumはタイプセーフな使い方をできるようです。


(おまけ)

staticブロック(初期化演算子)というものを初めて知ったのですが、面白い!

実際は使っちゃだめだと思うんですが、下のように宣言の前に代入するみたいな書き方もできました。配列やリストなどの組み合わせでもいろいろおもしろいことができそうです!

  static {
      num = 50;
  }
 
  public static final int num;



0 件のコメント:

コメントを投稿