Dartでリフレクション「MirrorSystem」

2012.09.02

毎度お世話になっております。クラスメソッドの稲毛です。
Dartに待望のリフレクション機構が追加されました。その名を「ミラーシステム(MirrorSystem)」といいます。
名前から「ソーラ・システム」や「反射衛星砲」のような架空兵器を想像してしまった人は同志です。是非Dartを使ってみましょう!

リフレクション対象のクラス

class Hoge {
  static final String STATIC_CONSTANT = 'STATIC_CONSTANT';
  static String STATIC_VARIABLE = 'STATIC_VARIABLE';
  static void staticMethod() {
    print('static method');
  }
  String variable;
  final String constant;
  Hoge(): variable = 'variable',
          constant = 'constant';
  void method() {
    print('Method called!');
  }
  String _privateVariable;
  String get accessor() => _privateVariable;
  set accessor(String value) => _privateVariable = value;
}

使用するには

MirrorSystem」はパッケージ「dart:mirrors」に用意されているので、利用する際には次のようにインポートを宣言します。

#import('dart:mirrors');

InstanceMirrorの取得

MirrorSystemをインポートするとグローバル関数「reflect」が使用できるようになります。この関数に任意のオブジェクトを渡すと、そのオブジェクトの「InstanceMirror」が得られます。

Hoge hoge = new Hoge();
  InstanceMirror instanceMirror = reflect(hoge);

ClassMirrorの取得

InstanceMirrorオブジェクトからクラスについてのメタデータを得るにはInstanceMirrorが持つ「type」を参照して「ClassMirror」オブジェクトを取得します。

ClassMirror classMirror = instanceMirror.type;

Mirrorオブジェクトの利用

メンバの情報が欲しい!

ClassMirrorオブジェクトが持つ「members」を参照します。

classMirror.members.forEach(
    (String key, DeclarationMirror value) {
      print(value);
    }
  );
VariableMirror on 'constant'
MethodMirror on 'accessor='
VariableMirror on 'STATIC_CONSTANT'
VariableMirror on 'STATIC_VARIABLE'
MethodMirror on 'staticMethod'
MethodMirror on 'Hoge'
MethodMirror on 'accessor'
MethodMirror on 'method'
VariableMirror on '_privateVariable'
VariableMirror on 'variable'

メンバのMirrorオブジェクトが得られました。

パブリックなインスタンス変数の情報が欲しい!

ClassMirrorが持つ「variables」の中から選別します。

classMirror.variables.forEach(
    (String key, VariableMirror value) {
      if (!value.isStatic && !value.isFinal && !value.isPrivate)
        print(value);
    }
  );
VariableMirror on 'variable'

パブリックなインスタンス変数のMirrorオブジェクトが得られました。

インスタンスから得られたInstanceMirror、InstanceMirrorから得られたClassMirror、ClassMirrorから得られたVariableMirrorやMethodMirrorというように、関連して得られるMirrorオブジェクトを利用してターゲットのメタデータにアクセスしていきます。

では最後に任意オブジェクトからそのクラスのインスタンスを生成してみます。

ClassMirrorオブジェクトからインスタンスを生成したい!

ClassMirrorの「newInstance」メソッドを使用して新しいInstanceMirrorオブジェクトを取得します。newInstanceメソッドの戻り値である「Future」オブジェクトは、「then」メソッドに指定した関数にInstanceMirrorオブジェクトを返しますので、そのInstanceMirrorオブジェクトの「reflectee」から新しいインスタンスを取得することができます。

classMirror.newInstance('', []).then(
    (InstanceMirror newInstanceMirror) {
      Hoge newHoge = newInstanceMirror.reflectee;
      newHoge.method();
      print('(hoge == newHoge) = ${hoge == newHoge}');
      print(newHoge.constant);
      print(newHoge.variable);
    }
  );
Method called!
(hoge == newHoge) = false
constant
variable

メソッドの実行も問題無く出来ていますし、元となったオブジェクトとは別のインスタンスであることもわかりますね。

まとめ

使い方を簡単に表現すると「オブジェクトをreflectして得られるMirrorオブジェクトから、知りたい情報のMirrorオブジェクトを見つけ出してゴニョゴニョする。」といった感じでしょうか。クロージャもオブジェクトですので、reflectするとInstanceMirrorのサブクラスである「ClosureMirror」を取得することが出来ます。
今後Dartの進化に併せてリフレクション機構も変化していく可能性もありますが、この「MirrorSystem」というのも面白い機構だなと思いました。(^^)