[Java]の列挙はCの列挙とは違うのだよ - Enum, EnumMap, EnumSet

列挙(列挙型)

Java は列挙もオブジェクトであり、特殊なクラスでございます。 クラスではあるけれど継承はできない。

列挙の実態は java.lang.Enum クラスを継承した特殊なクラスらしいけれど、 この java.lang.Enum クラスを継承するコードはコーダーが書くことはできない。 また enum で宣言した列挙には Serializable, Comparable インターフェイスが実装される。

最も簡潔な列挙型

enum EnumType01 {
  OBJ01, // = 0 みたいに値は指定できない。
  OBJ02,
  OBJ03; // 最後は セミコロン
}

EnumType01 という列挙のクラスについて、OBJ01, OBJ02, OBJ03 というインスタンスが勝手に生成されるという感じになる。Cenum とは全然違う。

interface ConstInterface {
  int VALUE01 = 0;
  int VALUE02 = 1;
  int VALUE03 = 2;
}

インターフェースでも似たような事が可能。こちらは Cenum 似ている。

そういや定数を利用するために 定数用のインターフェイスを実装しているクラスとか見かけたけど…それってどうなんだろう。実際どうなのかわからないけれど implements で実装させるのはなんか違和感を感じた。

列挙型にはメソッドを持たせられる

enum EnumType02 {
  ; // このセミコロンが存在しない場合はコンパイルエラー…えぇ~
  public void sayHello() {
    System.out.println("Hello");
  }
}

こんな感じで普通にメソッドを持てる。

フィールドとかコンストラクタとか

フィールド持たせて、そのフィールドにコンストラクタで値を設定できる。 コンストラクタの引数は 列挙したオブジェクトの後ろに括弧つけて値を指定する。

public class TestEnum01 {

  public static void main(String[] args) {
    String str;
    str = EnumType03.INSTANCE01.toString();
    str += "\n";
    str += EnumType03.INSTANCE02;
    str += "\n";
    str += EnumType03.INSTANCE03.object;
    System.out.println(str);
  }
}

enum EnumType03 {
  // インスタンスに渡す値を指定
  INSTANCE01(1,"A", new Date()),
  INSTANCE02(2,"B", new Exception()),
  INSTANCE03(3,"C", new Thread());

  int number;
  String string;
  Object object;

  // コンストラクタにより値が設定される
  EnumType03(int number, String string, Object object) {
    this.number = number;
    this.string = string;
    this.object = object;
  }

  @Override
  public String toString() {
    return number + string + object;
  }
}

結果

1ASun Jul 29 20:00:04 JST 2012
2Bjava.lang.Exception
Thread[Thread-0,5,main]

フィールドの可視性は private でもOK。あんまり意味がない気がするけどね…

Object のメソッド

列挙型にはデフォルトで #toString() など Object クラスから継承したメソッドが存在する。 なので以下の様なものも通る。

synchronized (EnumType01.VALUE01) {
  System.out.println(EnumType01.OBJ01.toString());
}

暗黙に定義されるフィールド name, ordinal

public class TestEnum02 {
  public static void main(String[] args) {

    EnumType04[] values = EnumType04.values();
    for (EnumType04 value : values) {
      System.out.println(value.name());
      System.out.println(value.ordinal());
    }

    /* 上のコードと下のコードの結果は同じとなる */
    // System.out.println(EnumType04.INSTANCE0.name());
    // System.out.println(EnumType04.INSTANCE0.ordinal());
    // System.out.println(EnumType04.INSTANCE1.name());
    // System.out.println(EnumType04.INSTANCE1.ordinal());
  }
}

enum EnumType04 {
  INSTANCE0,
  INSTANCE1;
}

結果

INSTANCE0
0
INSTANCE1
1

列挙で switch

public class TestEnum03 {
  public static void main(String[] args) {
    switch (EnumType05.INSTANCE) {
    case INSTANCE:
      // case EnumType05.INSTANCE はコンパイルエラー
      break;
    default:
      throw new IllegalStateException("こんな列挙定数ないっす");
    }
  }
}

enum EnumType05 {
  INSTANCE;
}

注意するのは case の後に続く値に クラス名を付けないことか。。

java.util.EnumMap

それぞれの列挙定数にたいする値をセットして使いたい時のコレクションクラス。

public class TestEnumMap {

  public static void main(String[] args) {
    // EnumMapのインスタンス生成
    EnumMap<EnumType06, String> enumMap;
    enumMap = new EnumMap<EnumType06, String>(EnumType06.class);

    // それぞれの定数に対して値を設定
    enumMap.put(EnumType06.I0, "Hoge ");
    enumMap.put(EnumType06.I1, "Piyo ");
    enumMap.put(EnumType06.I2, "Mogera ");

    // enumMap から値すべてを取り出して出力
    for (String value : enumMap.values()) {
      System.out.print(value);
    }

    // enumMap から 値を取り出して出力
    for (EnumType06 value : EnumType06.values()) {
      System.out.print(enumMap.get(value));
    }

  }
}

enum EnumType06 {
  I0, I1, I2
}

結果

Hoge Piyo Mogera Hoge Piyo Mogera

java.util.EnumSet

ビット和なフラグを使いたい時に使用する。

public class TestEnumSet {

  private enum Flag { A, B, C }

  public static void main(String[] args) {
    // EnumSet#of メソッドにより 列挙定数 のビット和としての値を生成できる。
    EnumSet<Flag> flags = EnumSet.of(Flag.A, Flag.C);

    for (Flag flag : Flag.values()) {
      if ( flags.contains(flag) ) System.out.println(flag);
    }

    System.out.println("------------------");

    // 生成した値の変更
    flags.add(Flag.B);
    flags.remove(Flag.A);

    for (Flag flag : Flag.values()) {
      if ( flags.contains(flag) ) System.out.println(flag);
    }
  }
}

結果

A
C
------------------
B
C

なんかまどろっこしい気がする

Share
関連記事