2011年7月25日月曜日

動的にクラスを読み込んで実行する。(配列を呼び出す方法。)

Javaの場合ClassLoaderにjarファイルを設定してあとからプログラムを読み込みさせて、プログラムを実行するという便利な機能があります。

たとえばtest.jarにあるEntryクラスのObject getData(String str, Integer i);を呼び出すなら
// ファイルを読み込む
File jarFile = new File("test.jar");
// クラスローダーにセット
ClassLoader loader = URLClassLoader.newInstance(new URLS[]{jarFile.toURI().toURL()});
// クラス取得
Class clazz = loader.loadClass(com.ttProject.Entry);
// クラスオブジェクト作成
Object inst = clazz.newInstance();
// メソッド取り出し
Method method = clazz.getMethod("getData", String.class, Integer.class);
// 実行
Object obj = method.invoke(inst, "test", 15);


こんな感じになります。
詳細はGoogleあたりで調べてもらうとして・・・問題は単一Argumentでかつ渡す値が配列のとき・・・この方法では動作しません。
Method method = clazz.getMethod("getData", String[].class);
Object obj = method.invoke(inst, new String[]{"arg1", "arg2"});
これを実行するとargumentが一致しないというエラーがでます。
なぜかというと、本来invokeの第2引数は引数の配列を渡すのが本来の形だからです。
上記の場合StringはObjectを継承しています。
よってnew String[]{"arg1", "arg2"}はnew Object[]{"arg1", "arg2"}と等価です。
となると、Stringの配列ではなく2つの文字列を引数として渡していることになり、元々のメソッドの引数は配列の形で1つという条件と当てはまらなくなり例外になるというわけです。

Method method = clazz.getMethod("getData", String[].class);
Object obj = method.invoke(inst, new Object[]{new String[]{"arg1", "arg2"}});
としてやれば、JavaVMがただしく理解してきちんと動作します。
Object[]{}で囲っていやれば、よいというわけです。

余談ですが、getMethod側も実はただしい書き方があります。
Method method = clazz.getMethod("getData", new Class[]{String[].class});
と書くのが正解なようです。

このあたりの動作を正しく理解しておけば、いろいろなクラスが自由自在につかえるようになるでしょう。

さて、red5をGUIで実行できるようにする最後の追い込み、Bootstrapの拡張の作成するか・・・

0 件のコメント:

コメントを投稿