列挙型をクラスで実現
Javaには列挙型がありません。C/C++に慣れ親しんでいる方なら、これはちょっと不便ですよね。 たとえばあるオブジェクトの状態を、「動いている」、「休んでいる」、「準備している」という ユーザ定義の状態で表したくなったらどのように実現するでしょうか? 1つはint型の変数で状態を表す方法がありますが、これでは「1」という数字が、 上のどの状態を表しているのかすぐには分かりません。

Javaに列挙型がないのは、クラスを使って実現可能だからです。ここではその方法を説明します。

まず思いつくのが、クラスにpublicでfinalな静的フィールドを定義する方法です。 例えば、上の例について、人間を表すManクラスを定義して、 その人は必ず、「動いている」、「休んでいる」、「準備している」の3つの状態を持つものとします。 この3つの状態をpublicでfinalな静的フィールドを用いて表現すると下のようになります。

			
class Man {
    public final static int WORK = 0;
    public final static int REST = 1;
    public final static int READY = 2;
}

この方法では一見何の問題もないように思います。finalですから値の書き換えも出来ませんし、 publicでstaticなので、インスタンス生成なしでどこからでも参照出来ます。

ただし、この方法には決定的な欠点があるのです。それは、WORKやRESTは、 人の状態を定義した型ではなく、あくまでint型であるという事です。 これによって引き起こされる問題は、人の状態を必要としているところに、int型の変数を 渡せてしまうということです。同時に、int型を必要としているところなら、 WORKやRESTを書いても正しく動いてしまいます。 これらの変数には人の状態を表す以外は何の役割もないはずですから、 そのように動作出来てしまうことは避けなくてはなりません。

では具体的にどうしたらいいのでしょうか?

人の状態を定義した型が欲しいのですから、人の状態を表すクラスを作る必要があります。 ここではManStateクラスとしましょう。このクラスは人の状態以外は表しません。 人の状態は、「動いている」、「休んでいる」、「準備している」の3つしかありませんから、 この3つだけ表せばいいのです。

ここまででソースを思いついた人がいたらすごいですね。ソースは以下のようになります。

			
/*人の状態を表すクラス*/
final class ManState {
    public final static ManState WORK = new ManState();
    public final static ManState REST = new ManState();
    public final static ManState READY = new ManState();

    private ManState() {}
}

/*人を表すクラス*/
class Man {
    /*人の状態を変える*/
    public void setHisState(ManState state) {
        ...
    }
}

この列挙型を実現するクラスのポイントは2つあります。1つ目はコンストラクタをprivateにしていることです。 普通、コンストラクタをprivateにすることは滅多にありませんが、 Singletonパターンなどでは、 コンストラクタをprivateにします。これはまさにSingletonパターンの応用です。 コンストラクタをprivateにすることで、外部からはインスタンス生成が出来なくなります。 つまり、インスタンスを作れるのはこのクラス内だけです。 2つ目は列挙すべき要素を自身の型の変数としているところです。 これはちょっと奇妙ですよね。クラスの定義中にそのクラス名を使ってしまうんですから。 しかし、これはとても優れた使い方です。何故なら、コンストラクタがprivateなので、 このクラスを使用するプログラム中のManState型のオブジェクトは、WORKとRESTとREADYの3つしか 存在しないことが確実に保証されるからです。言い換えれば、人の状態を表すには、 この3つのstaticフィールドを参照するしかないということです。

これは最初に示した方法とは明らかに違います。最初の方法だと、その気になれば 別名を付けて人の状態を表すことが可能でした(たとえば、int IS_WORK = 1)。 しかも結局はint型の変数だったので、人の状態を与えるべきところにも、 整数を放り込んでやればコンパイルされることもあるかもしれません。

ところが、ManStateクラスを上記のように使用すると、人の状態を与えるべき Man.setHisStateメソッドには、「ManState.WORK」、「ManState.REST」、 「ManState.READY」、の3つしか渡せません。ManState型以外はコンパイルエラーになりますが、 ManState型のコンストラクタはprivateなので、上記以外のManState型のインスタンスは存在し得ないのです。

これでJavaでもCやC++の列挙体を実現することが出来ます。ただし、この方法にも欠点があって、 このクラスを利用したswitch文が使えません。Javaのswitch文は整数型と char型にしか適用できないからです。ですが、JDK1.5では、switch文を利用可能な列挙型が 追加される予定です。気長に待ちましょう。