- ベストアンサー
ClassLoaderを作成してみたいのですが
クラスから別の場所のクラスを呼び出す構造を作りたくて、ClassLoaderを勉強しています。 まず、ClassLoaderを継承したLoaderを作成しなくてはいけないというのがわかり、その中で抽象メソッドloadClassと、関係するdefine,resolve,findSystemなどを記述してやるらしいというのまでは、あやふやながら理解できました。 それで、呼び出す側では、Loader.loadClassを呼んでクラスをロードし、newInstanceメソッドを呼べば、その実体ができる"らしい"まで解読して頭がパンクしてしまいました。 疑問は、以下の通りです。 ・「Loaderは自分で作らないといけない」とあったのですが、ClassLoaderを継承させたLoaderを自前で書かないといけないのでしょうか。単にオブジェクトを生成するだけでいいんでしょうか。簡単なサンプルがあれば嬉しいのですが・・・。 ・ ひとまず、簡単な計算を行うクラスを作って呼び出してみたいのですが、newInstanceを実行するとインスタンスが生成されて即中身が実行される、という説明文を読んだのですが、値の受け渡しはどうすればできるのでしょうか。 以上、よろしくご教示お願いいたします。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
大抵は URLClassLoader をインスタンス化するだけで用が足ると思います。 たとえば C:/classes からクラスを読込むのであれば、 URLClassLoader loader = new URLClassLoader( new URL[ ]{ new File("c:/classes").toURL() } ); でロードするクラスを完全限定名で指定して、 Class clazz = Class.forName("sample.Hoge", true, loader); そしてインスタンス化。 Object obj = clazz.newInstance(); ただここからが問題で、 このオブジェクトは sample.Hoge 型に キャストできません。 このオブジェクト特有のメソッドを実行したいのなら、 リフレクションを使います。 Method method = clazz.getMethod( "メソッド名", new Class[0] ); // 引数なし method.invoke( obj, new Object[0] );
その他の回答 (2)
- mojimojio
- ベストアンサー率51% (14/27)
1コ前の者です。 投稿した後に気づいたのですが、「多くの場合は問題になりません」はウソで、けっこう引っかかりやすい部分だと思ったので補足を。 public class Main { public static void main(String[] args) { ClassLoader loader = new MyClassLoader(); Class clazz = Class.forName("hoge.Hoge", true, loader); HogeInterface hoge = (HogeInterface)clazz.newInstance(); hoge.start(); } } この例の場合、Mainクラス(のクラスローダ)はhoge.Hogeクラスを知らないので、hoge.Hogeにキャストすることはできません。(もし起動クラスパスにhoge.Hogeがあったとしても、MyClassLoaderがロードすれば別のクラスになります) このような場合、Mainクラスが知っているクラスなりインタフェースにキャストしてやるようにすると良いと思います。hoge.HogeはHogeInterfaceをimplementsする。 もちろん、#1の方の回答のようにリフレクション経由でのメソッド呼び出しも可能です。 Hogeクラスやその中の呼び出しではクラスローダがMyClassLoaderなので、いくらでもHogeクラスを使用することができます。
お礼
あれからちょっと考えまして、リフレクションでメソッドを呼んだり試行錯誤して、少しずつわかってきました。 ひとまず簡単なものを書いてみて、参照ができるということが確認できましたので、徐々に理解を深めてみようと思います。ありがとうございました。
- mojimojio
- ベストアンサー率51% (14/27)
以下、私の理解の範囲で。 ・自前クラスローダからロードした場合であっても、Class#newInstanceで作ったオブジェクトをキャストすることはできます。 ただし、他でシステムクラスローダがhoge.Hogeをロードした場合、自前クラスローダのhoge.Hogeクラスとは同じ名前の別のクラスになります。(キャストしたときにはClassCastExceptionが発生する) しかし、クラスのロードには、その参照元クラスのクラスローダが使われますので、多くの場合は問題になりません。 class Hoge { static Hige hige; } となっていた場合、HigeクラスのロードはHogeをロードしたクラスローダが行います。 ・クラスロードの処理に何かを噛ませることが目的でしたら、URLClassLoaderを継承することができます。 また、そもそもURLで表せないものからクラスをロードしたい場合は、ClassLoaderを継承するのが良いと思います。 一度ロードしたクラスのキャッシュ処理などはClassLoaderの中にありますので、自前クラスローダはfindClassをオーバーライドするだけで済みます。 (リソースのロードにも対応するためには、他にも実装が必要になりますが) ・Class#newInstanceは引数なしのコンストラクタを呼びます。引数を渡したい場合には、java.lang.reflect.Constructorを使います。
お礼
ありがとうございます。クラスローダを自前で作る理由というのがわからなかったので、URLClassLoaderを生成してみました。 ロードしたクラスに引数を渡せるというのはなんとなく理解できたのですが、具体的な方法(必死にリファレンスを読んではいるのですが、サンプルが少なくて・・・)やロードしたクラスから呼び出し元と同じ場所に配置してあるクラスを呼べるのかといったことが結局まだわからないので・・・もっと勉強してみます。
お礼
コメントありがとうございます。ClassLoaderが直接生成できないワケがどうしてもわからなかったので、URLClassLoaderでサンプルを作成してみました。結果は上手くいったのですが、ここで作ったオブジェクト(中身はロードしたクラスの実体、でいいんでしょうか)の触り方がいまいちわからないんです。生成した瞬間にクラスが実行されるようですが、呼び出し元とは関係ない空間で動いているのでしょうか。 リフレクションというのがあるらしい、は分かるのですが、いまいちパッとしなくて・・・すみません; (method.invokeで引数を渡して、同時に戻ったオブジェクトを取り出せるのでしょうか)