教育機関によるプログラミング教育を受けていないので、どちらかといえばJavaやC++のような言語よりもSmallTalk属のオブジェクト指向(Ruby, Python, Objective-Cです)に馴染みがあり、時々どうしてもクラスそのものをオブジェクトとして扱いたくなってしまいます。
HaxeのことをC#風OCamlだと捉えていたので、(そもそもOCamlのオブジェクト指向がどういったシステムなのかは知らないが)メタクラス的な機構は存在しないと思い込んでいました。
が、そこは流石マクロてんこ盛りのメタプロし放題の変態言語だけあって普通に使えました。しかもJavaのリフレクションみたいにややこしい書き方をする必要もないし、補完や型推論も普通に動きます。
Type.createInstance(HogeClass, [arg1, arg2, ...]);
// same as new HogeClass(arg1, arg2);
これだけです。超楽ですね。Type
は最初から名前空間に存在する方のモジュールで、haxe.macro.Type
とは別のモジュールであることに注意してください。
他にもcreateEnum
などもメソッドを持ちます。詳しくは http://api.haxe.org/Type.html を参照してください。
さて、インスタンス化のやり方はわかったら今度はクラスオブジェクトそのものの型宣言です。
var cls: Class<HogeClass> = HogeClass;
Type.createInstance(cls, [x, y, z]);
// using Type; すれば cls.createInstance([x, y, z]); とも書ける
Class<T>
を使えば良いわけですね。簡単です。
こんなことをして何が嬉しいんだと思われる向きもあるとおもうので、無理矢理ユースケースを考えます。
using Type; interface IDisplayable { public function show(): Void; public function hide(): Void; } class DisplayableHelper { public static function displayWhileFunction<T: IDisplayable>(cls: Class<T>, prepare: T -> Void, func: Void -> Void): Void { var disp = cls.createInstance([]); prepare(disp); disp.show(); func(); disp.hide(); } }
解説です。
例えばダイアログ的なモノを作るとして、派生クラスが色々あるとします。
派生がゴチャゴチャとある中で、「処理中だけなんかいい感じに表示したい」みたいな共通処理的なコードを書きたい気持ちになってください。しかも、その処理はインスタンスメソッドではなく、スタティックメソッドにしたいのです。どうでしょう、なれましたか?
HaxeにおいてはInterfadeにスタティックメソッドを宣言することは出来ません。なんかVB.NETとかいう言語はInterfaceにスタティックメソッドを宣言、実装することが出来た気がするのですが多分気のせいです。
勿論、Interfaceを継承させる先で、どこか適当なところに抽象基底クラスを作れば良いわけですが、継承というのは煩雑な状態管理などで余計な複雑さを生み出す元凶なのでこの程度のことであっても使いたくありません。そこでメタクラスです。
public static function displayWhileFunction<T: IDisplayable>(cls: Class<T>, prepare: T -> Void, func: Void -> Void): Void
は、まずジェネリックな型変数T
をIDisplayable
で制限しています。
cls
はnewしたいクラスそのものです。
さらに、汎用性を高めるためにnewした後のオブジェクトそのものの前準備をラムダ式で渡せるようにprepare
を宣言します。T
の型はIDisplayable
で束縛されているため、当然のようにprepareで渡すラムダ式内ではちゃんと補完も型推論も効きます。本筋とは関係ないですが、型変数T
を使っているおかげで、例えばbefore
みたいな引数を増やしたくなっても、IDisplayable -> Void
みたいに書かずにT -> Void
みたく書けるのはdryで良いですね。ジェネリクスって素晴らしいですね。
あとはDisplayableHelper
をmixinするだけで継承先全てのクラス、例えばclass MessageDisplay implements IDisplayable {}
などにdisplayWhileFunction
というスタティックメソッドが生えます(生えたように見えます)。
いじょう、だいたいそんな感じです。
便利昨日っぽい雰囲気を出してはいますが、もちろんお察しの通りcreateInstance
は変数初期化時のコンパイル時検査をすり抜けてランタイムエラーを起こしたりなんだりとHaxeの型安全な良さを損なう機能です。
損なう機能ではあるんですけど、「色々な言語に変換出来る」というHaxeの性格上、問題解決の手駒としてメタプロや黒魔術の知識は多ければ多いほど良いわけですね。使いどころがないには越したことがないのですけれども、現実の作業はどうしても泥臭いです。
おしまい!