もう1つ列挙型の例を見てみましょう。int enumパターンでは複数の値をまとめて管理できませんが、メソッドやフィールドを持つ列挙型を使うことにより実現できます。ここでは、列挙定数の文字列だけでなく、日本語でも各月の名前を表示できるようにしてみます。
package sample21; public class EnumJapaneseMonthList { enum Month { JANUARY("睦月"), FEBRUARY("如月"), MARCH("弥生"), APRIL("卯月"), MAY("皐月"), JUNE("水無月"), JULY("文月"), AUGUST("葉月"), SEPTEMBER("長月"), OCTOBER("神無月"), NOVEMBER("霜月"), DECEMBER("師走"); private String name; Month(String name) { this.name = name; } public String getName() { return name; } } public void exec() { for (Month m : Month.values()) { System.out.println(m + ":" + m.getName()); } } public static void main(String[] args) { EnumJapaneseMonthList app = new EnumJapaneseMonthList(); app.exec(); } }
JANUARY:睦月 FEBRUARY:如月 MARCH:弥生 APRIL:卯月 MAY:皐月 JUNE:水無月 JULY:文月 AUGUST:葉月 SEPTEMBER:長月 OCTOBER:神無月 NOVEMBER:霜月 DECEMBER:師走
EnumJapaneseMonthListクラス内のMonth型では、文字列を引数とするコンストラクタを用意して、そこで与えられた文字列をnameフィールドへ保持しています。
列挙定数と対応するメッセージを一緒に管理したり、列挙定数と対応する整数値やオブジェクトを一緒に管理したりしたい場合があります。そんなときは、「java.util.EnumMap」クラスを使います。
次の例では、各月の名前と、整数値を対応させて、EnumMapを使って一緒に管理しています。int enumパターンで実装した「2011年の各月に何日あるか表示するプログラム」と同じ動作をするプログラムです。
package sample21; import java.util.EnumMap; public class EnumMapMonthList { private enum Month { JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER; } private EnumMap<Month, Integer> monthMap; public EnumMapMonthList() { monthMap = new EnumMap<Month, Integer>(Month.class); monthMap.put(Month.JANUARY, 1); monthMap.put(Month.FEBRUARY, 2); monthMap.put(Month.MARCH, 3); monthMap.put(Month.APRIL, 4); monthMap.put(Month.MAY, 5); monthMap.put(Month.JUNE, 6); monthMap.put(Month.JULY, 7); monthMap.put(Month.AUGUST, 8); monthMap.put(Month.SEPTEMBER, 9); monthMap.put(Month.OCTOBER, 10); monthMap.put(Month.NOVEMBER, 11); monthMap.put(Month.DECEMBER, 12); } public void exec() { System.out.println("2011年"); for (Month m : Month.values()) { String s = monthMap.get(m) + "月"; switch (m) { case FEBRUARY: s = s + ":" + m + "(28日)"; break; case APRIL: case JUNE: case SEPTEMBER: case NOVEMBER: s = s + ":" + m + "(30日)"; break; case JANUARY: case MARCH: case MAY: case JULY: case AUGUST: case OCTOBER: case DECEMBER: s = s + ":" + m + "(31日)"; break; } System.out.println(s); } } public static void main(String[] args) { EnumMapMonthList app = new EnumMapMonthList(); app.exec(); } }
switch文の判別式に、列挙型の変数を指定している点、case文では列挙定数を指定する点に注目してください。
2011年 1月:JANUARY(31日) 2月:FEBRUARY(28日) 3月:MARCH(31日) 4月:APRIL(30日) 5月:MAY(31日) 6月:JUNE(30日) 7月:JULY(31日) 8月:AUGUST(31日) 9月:SEPTEMBER(30日) 10月:OCTOBER(31日) 11月:NOVEMBER(30日) 12月:DECEMBER(31日)
ここでは、数値を対応させていますが、例えば、JANUARYに「JAN.」という略称を対応させたい場合は、「EnumMap
列挙定数にビット演算しやすい値を指定したいときがあります。
例えば、上下左右の矢印キーを組み合わせて使えるようにしたいとします。これは、int enumパターンでの実装なら、各キーの値を下記のように対応させることにより、ビット和操作などができるようになります。
package sample21; import java.util.ArrayList; import java.util.List; public class ArrowKeysSample { class ArrowKeys { public static final int UP = 0x01; public static final int RIGHT = 0x02; public static final int DOWN = 0x04; public static final int LEFT = 0x08; } public void exec() { List<Integer> keys = new ArrayList<Integer>(); keys.add(ArrowKeys.UP); keys.add(ArrowKeys.RIGHT); keys.add(ArrowKeys.UP+ArrowKeys.RIGHT); keys.add(ArrowKeys.DOWN); keys.add(ArrowKeys.UP+ArrowKeys.DOWN); keys.add(ArrowKeys.UP+ArrowKeys.RIGHT + ArrowKeys.DOWN+ArrowKeys.LEFT); for (int e : keys) { System.out.print(e + ":"); if ((e & ArrowKeys.UP) != 0) { System.out.print("UP, "); } if ((e & ArrowKeys.RIGHT) != 0) { System.out.print("RIGHT, "); } if ((e & ArrowKeys.DOWN) != 0) { System.out.print("DOWN, "); } if ((e & ArrowKeys.LEFT) != 0) { System.out.print("LEFT, "); } System.out.println(""); } } public static void main(String[] args) { ArrowKeysSample app = new ArrowKeysSample(); app.exec(); } }
1:UP, 2:RIGHT, 3:UP, RIGHT, 4:DOWN, 5:UP, DOWN, 15:UP, RIGHT, DOWN, LEFT,
このプログラムでは、例えば、「UPの矢印キーが押下されているときは1」「UPとRIGHTの矢印キーが同時に押下されているときは3」「全部が押下されているときは15」となっていて、どのキーが押されているかが、int値から分かります。「UP」「RIGHT」といった定数がint値の各ビットと対応付けされているので、int値の対応するビットが0なのか、1なのかで、押下を判定できるのです。
同様なプログラムを、int enumパターンではなく列挙型でできないでしょうか。実は、Javaでは「java.util.EnumSet」クラスが用意されていて、実現可能です。
package sample21; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; public class EnumArrowKeysSample { enum ArrowKeys { UP, RIGHT, DOWN, LEFT } public void exec() { List<EnumSet<ArrowKeys>> keys = new ArrayList<EnumSet<ArrowKeys>>(); keys.add(EnumSet.of(ArrowKeys.UP)); keys.add(EnumSet.of(ArrowKeys.RIGHT)); keys.add(EnumSet.of(ArrowKeys.UP, ArrowKeys.RIGHT)); keys.add(EnumSet.of(ArrowKeys.DOWN)); keys.add(EnumSet.of(ArrowKeys.UP, ArrowKeys.DOWN)); keys.add(EnumSet.allOf(ArrowKeys.class)); for (EnumSet<ArrowKeys> e : keys) { System.out.print(e + ":"); if (e.contains(ArrowKeys.UP)) { System.out.print("UP, "); } if (e.contains(ArrowKeys.RIGHT)) { System.out.print("RIGHT, "); } if (e.contains(ArrowKeys.DOWN)) { System.out.print("DOWN, "); } if (e.contains(ArrowKeys.LEFT)) { System.out.print("LEFT, "); } System.out.println(""); } } public static void main(String[] args) { EnumArrowKeysSample app = new EnumArrowKeysSample(); app.exec(); } }
[UP]:UP, [RIGHT]:RIGHT, [UP, RIGHT]:UP, RIGHT, [DOWN]:DOWN, [UP, DOWN]:UP, DOWN, [UP, RIGHT, DOWN, LEFT]:UP, RIGHT, DOWN, LEFT,
このプログラムでは、EnumSet型のオブジェクトで矢印キーの押下を表現しています。
例えば、UPの矢印キーが押下されているときは「EnumSet.of(ArrowKeys.UP)」、UPとRIGHTの矢印キーが同時に押下されているときは「EnumSet.of(ArrowKeys.UP, ArrowKeys.RIGHT)」、全部が押下されているときは「EnumSet.allOf(ArrowKeys.class)」、となっています。
ある値を列挙してプログラムで利用したい場合は、Javaでは列挙型を使えばいいことが理解できたでしょうか。Javaのバージョンによっては(5よりも前の場合)列挙型が使えませんから、その場合はタイプセーフenumパターンを使います。
また、サンプルプログラムでちょっとした値を列挙したい場合や、列挙型を使うまでもない場合は、int enumパターンが使えます。作成したいプログラム、Javaのバージョンといったプログラミング環境に合わせて、どれを使うか決めるといいでしょう。
本記事では、列挙型の基本的な使い方について説明しましたが、インターフェイスを実装したり、独自のメソッドやフィールドを宣言して使う方法もあります。Javaの列挙型はいろいろな場面で利用できますから、興味がある読者は、enumについてさらに調べてみてください。
次回は、Java 5からの肝といえる「アノテーション」について説明する予定です。今回作ったサンプルのソースコードはこちらからダウンロードできます。
小山博史(こやま ひろし)
情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。長野県の地域コミュニティである、SSS(G)やbugs(J)の活動へも参加している。
著書に「基礎Java」(インプレス)、共著に「Javaコレクションフレームワーク」(ソフトバンククリエイティブ)、そのほかに雑誌執筆多数。
Copyright © ITmedia, Inc. All Rights Reserved.