リフレクションの利用

Curlでのリフレクションの使用方法について記載していきます。そもそもリフレクションとはCurlのクラスからメソッドやゲッターの情報を取得するAPIのことです。また、取得したメソッド等を実行することができます。

リフレクションを利用するためには、CURL.LANGUAGE.REFLECTIONというパッケージをインポートする必要があります。

{import * from CURL.LANGUAGE.REFLECTION}

まず当機能を説明するにあたって以下のクラス(Foo)を例にとっていきたいと思います。 

{curl 6.0 package}
{package COM.CURLAP.TEST}

{define-class public open Foo

  field private _a:String
  field private _b:int

  {getter public {a}:String
    {return self._a}
  }

  {getter public {b}:int
    {return self._b}
  }

  {constructor public {default
                          a:String,
                          b:int
                      }
   
    set self._a = a
    set self._b = b
  }

  {method public open {display}:void

    {dump self._a, self._b}
  }
}

文字列からインスタンス作成

文字列のパッケージ名やクラス名からオブジェクトを生成する方法を下のソースコードをもとに説明していきます。

文字列(”COM.CURLAP.TEST”)から、Packageクラス(パッケージのクラスです。)のオブジェクトを生成するため、ComponentSelectorを利用します。この生成されたPackageオブジェクトから、その中に含まれるクラスやプロシージャを取得するため、get-memberメソッドを文字列のクラス名(”Foo”)を指定して実行します。get-memberメソッドはPackageMemberをかえします。

このPackageMemberのget-valueを利用し、ClassTypeクラスのオブジェクトを取得します。そのClassTypeからget-constructorを用いてConstructorを取得できますので、newメソッドを利用し、Fooクラスのオブジェクトを生成できます。

    || Package
    let p:Package =
        {import-package
            {ComponentSelector name = “COM.CURLAP.TEST”}
        }
    
    || PackageMember
    let m:#PackageMember = {p.get-member “Foo”}

    || ClassType
    let t:ClassType = {m.get-value}

    || Constructor
    let c:#Constructor = {t.get-constructor “default”}

    || オブジェクトの生成
    {dump {c.new “xxxx”, 999}}

オブジェクトからクラス名等の名前を取得

オブジェクトからクラス名やメソッド名、ゲッター・セッター名等を取得する方法を下のソースコードをもとにご説明します。

最初に、Fooオブジェクトを通常通り生成します。このFooオブジェクトのゲッターとメソッドの情報をリフレクションを利用し、取得することとします。

リフレクションの利用にあたってまずはFooオブジェクトのClassTypeオブジェクトをtype-ofマクロを用いて取得します。パッケージ名を取得するには、このClassTypeオブジェクトのpackage.nameアクセッサーを利用することで取得できます。次のゲッターやメソッドを取得するため、ClassType.get-membersメソッドを利用します。この際、ゲッターならClassMember.getter-filterをメソッドならClassMember.method-filterをキーワード引数のfilterに指定します。これによりすべてのゲッター名やメソッド名を取得することができます。

また、上記で取得した名前を元に、{ClassType.get-getter ゲッター名}でGetterオブジェクトを取得し、そのget-valueメソッドで値を主時できますし、{ClassType.get-method “メソッド名”}でMethodオブジェクトを取得し、invokeメソッドでメソッドを実行することができます。

    let foo:Foo = {Foo “foo-foo-foo”, 1234}
   
    || ここからリフレクション!
    let t:ClassType = {type-of foo} asa ClassType
    {dump t.package.name} || パッケージ名の表示

    || getter
    {for m in {t.get-members
                  filter = ClassMember.getter-filter}
     do
        def getter-name = m.name
        def getter:Getter = {t.get-getter getter-name} asa Getter
        {dump getter-name, {getter.get-value foo}} || ゲッター名とその値の表示
       
    }

    || method
    {for m in {t.get-members
                  search-superclasses? = false,
                  filter = ClassMember.method-filter}
     do
        def method-name = m.name
        def method:Method = {t.get-method method-name} asa Method
        {dump method-name, {method.invoke foo}} || メソッド名とその値の表示
       
    }

もちろんその他、セッターやプロシージャ等々の情報も取得し、実行することができます。

関連ドキュメント

リフレクション