「言語拡張」カテゴリーアーカイブ

ダイナミックインポートの利用

ダイナミックインポートを使うことで、”必要なときだけ”必要なパッケージをインポートすることができます。これによって、アプレット起動などのパフォーマンスを向上させることができます。

以下が例となります。

        || pはPackageクラス(ここではCOM.CURL.CDK.SQLITEパッケージをインポート)
        def p = {import-package {make-package-selector “COM.CURL.CDK.SQLITE”}}
        || SQLiteDatabaseクラスを取得
        def SD = {p.lookup “SQLiteDatabase”, check-imports? = true}
        def the-database = {SD {url “test.db”}} || SQLiteDatabaseのインスタンス生成(コンストラクタの呼出)
        {the-database.execute
            |”
               CREATE TABLE IF NOT EXISTS orb_simple_cache (
                 name VARCHAR,
                 key VARCHAR,
                 value BLOB,
                 PRIMARY KEY (name, key)) “|

        } || SQLiteDatabase.executeメソッドの呼出

ダイナミックインポートを利用するには、import-packageを利用して、Packageクラスのオブジェクトを取得します。(ちなみにその引数で対象のパッケージを選択するために、make-package-selectorを使うことができます。) 

注意) このとき、上記の方法の場合、マニフェストにパッケージを指定する必要があります。マニフェストにパッケージを記述したくない場合は、import-packageのキーワード引数のmanifestにパッケージファイルのurl(パス)を指定します。

対象のPackageオブジェクトから、そのプロシージャやクラスを取得するには、Package.lookupを利用します。(もしパッケージに新しいプロシージャやクラスなどを追加したい場合は、Package.addを使うことで動的に追加できます。)

また以下の例のようにevaluateプロシージャ内でもダイナミックインポートしたパッケージをPackageオブジェクト(正確にはOpenPackage)を利用できます。(evaluatepackageキーワード引数)

        || pはPackageクラス(ここではCOM.CURL.CDK.SQLITEパッケージをインポート)
        def p = {import-package {make-package-selector “COM.CURL.CDK.SQLITE”}}
        || evaluateでも利用できます。
        {evaluate
          || ここにコード
          , package = p asa OpenPackage
        }

 その他関連APIとして、dynamic-lookupなどを参照ください。

 

リフレクションの利用

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}} || メソッド名とその値の表示
       
    }

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

関連ドキュメント

リフレクション