列挙型(enum)
列挙型は有限個のコンストラクタ(列挙子)を持った、クラスとは異なった構造です。
例:
enum Color { Red; Green; Blue; } class Colors { static function toInt( c : Color ) : Int { return switch( c ) { case Red: 0xFF0000; case Green: 0x00FF00; case Blue: 0x0000FF; } } }
列挙型は指定されたコンストラクタ(列挙子)以外の値を取ることが出来ないので、それらの確実性を担保したい時に向いています。
コンストラクタパラメータ
上のColor列挙型のサンプルコードでは3つの定数コンストラクタが列挙型に与えられていました。
以下のようなパラメータを持ったコンストラクタも同様に扱うことができます。
enum Color2 { Red; Green; Blue; Grey( v : Int ); Rgb( r : Int, g : Int, b : Int ); }
この場合、Color2列挙型には無限のパターンがありますが、全ていずれかのコンストラクタに分類されることは変わりません。
以下に示す値は全てColor2列挙型となります。
Red;
Green;
Blue;
Grey(0);
Grey(128);
Rgb( 0x00, 0x12, 0x23 );
Rgb( 0xFF, 0xAA, 0xBB );
次のAlphaコンストラクタのように、再帰的なデータ型も与えることができます。
enum Color3 { Red; Green; Blue; Grey( v : Int ); Rgb( r : Int, g : Int, b : Int ); Alpha( a : Int, col : Color3 ); }
下の2つは共にColor3列挙型の値です。
Alpha( 127, Red ); Alpha( 255, Rgb(0,0,0) );
コンストラクタのパラメータは読み込み専用です。
すなわち、値を初期化した後にパラメータを書き換えることはできません。
列挙型でSwitch
switchには列挙型のための特殊な機能が用意されています。defaultケースが無い場合、列挙型すべてのコンストラクタがswitchによってチェックされます。もし、コンストラクタのケースが足りていない場合コンパイラが警告します。
例えば、初めのColor列挙型の場合で以下のコードを考えます。
switch( c ) { case Red: 0xFF0000; case Green: 0x00FF00; }
このコードはBlueのケースが無いためコンパイルエラーになります。このエラーでは、Blueのケースかdefaultを追加することで回避できます。
この仕組みはとても便利です。たとえば、列挙型に新しいコンストラクタを追加した場合にコンパイルエラーが発生するので、新しいコンストラクタに対する処理がどこで必要とされているかを知ることができます。
コンストラクタパラメータでswitch
列挙型のコンストラクタにパラメータがある場合、switchのケースではそれらすべてに対して変数名を定義しなければなりません。これらの変数はcaseの範囲内のみで使用可能で、列挙型のコンストラクタパラメータが割り当てられます。
Color3を使った例です。
class Colors { static function toInt( c : Color3 ) : Int { return switch( c ) { case Red: 0xFF0000; case Green: 0x00FF00; case Blue: 0x0000FF; case Grey(v): (v << 16) | (v << 8) | v; case Rgb(r,g,b): (r << 16) | (g << 8) | b; case Alpha(a,c): (a << 24) | (toInt(c) & 0xFFFFFF); } } }
switchを使った場合のみ、コンストラクタのパラメータへアクセスすることができます。
列挙型の型パラメータ
列挙型も型パラメータを持つことが可能です。構文について、以下に列挙型をセルに使ってリンクリスト作る例を示します。
enum Cell<T> { Empty; Cons( item : T, next : Cell<T> ); } class List<T> { var head : Cell<T>; public function new() { head = Empty; } public function add( item : T ) { head = Cons(item,head); } public function length() : Int { return cellLength(head); } private function cellLength( c : Cell<T> ) : Int { return switch( c ) { case Empty : 0; case Cons(item,next): 1 + cellLength(next); } } }
列挙型とクラスをあわせて使う手法、いくつかのケースでは非常に強力です。
パラメータのデフォルト値として列挙型を使う
列挙型の値はコンストラクタから生成されるので、定数にはなりません。そのため、パラメータのデフォルト値として定義できません。しかし、簡単な解決策があります。
enum MyEnum { MyFirstValue; MySecondValue; } class Test { static function withDefaultValuesOnParameters(?a : MyEnum) { if(a == null) a = MyEnum.MyFirstValue; } }
列挙型と文字列を相互変換する
Type.createEnum()がStringから列挙型に戻す際に利用可能です。enum EColor { Red; Green; Blue; } // To string: var string = Std.string(EColor.Blue); // From string: var color:EColor = Type.createEnum(EColor, string);
列挙型の比較
二つの列挙型が同じであるか確認するためにはType.enumEq()を使います。(==演算子の動作は保障されません、経験的にはパラメータを持たない列挙型の値に対しては働きますが、パラメータを持つ場合に失敗します。Type.enumEq()を使えばどちらに対しても正しく働きます)
備考
Haxeの列挙型はtagged unions(Wikipedea)に似た挙動を持ち、与えられた結果の全ての場合を特定し捕捉することを可能にします。これはステートメント内で列挙型の取りうる値すべてが特定されているためであり、価値あることです。
«« 型パラメータ - パッケージ(package)とインポート(import) »»