Javaをマニアックに楽しむ方法

Javaには,実行中に任意のクラスをダイナミックロードできる。

<Javaを使っていないひとには,ぜんぜん面白くない記事です(--;。タブとか空白や  を入れても空白が入らない仕様なのでインデントされていません>

Javaはコンパイルすると,クラス名.class を出力する。
通常クラスを使う場合は,クラス名を書いてやったり,importで指定してやれば
使うことが出来る。

じゃあ,実行中に,任意のクラスをロードできないか?
例えば,クラス名をキーボードから入力し,そのクラスのフィールドやメソッドの一覧や
さらには,そのクラスのオブジェクトを作る方法は?



ObjectクラスとかClassクラスとかFieldクラスがある

import java.lang.reflect.Member;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

// 実験用のクラス。このクラスをロードしています。
class TestClass
{
public int num;
public double gas;
public static int static_val;
public void Proc(int a, String s)
{
System.out.println("a = "+ a + " s = "+s);
}
public static void TestProc2(double a, String s)
{
}
}

class DLoadClass
{
public static void main(String[] args) throws Exception
{
Class c = Class.forName("TestClass"); // 上記のclass TestClass のクラス情報ロード
Constructor cst[] = c.getDeclaredConstructors(); // コンストラクタ一覧取得。getConstructors じゃないのは,デフォルトのコンストラクタも取りたいので
Field f[] = c.getFields(); // フィールド一覧取得
Method m[] = c.getMethods(); // メソッド一覧取得

int i,j;

System.out.println(c.getName() + "クラスの情報");

/* フィールド一覧を出力 */
System.out.println("フィールド一覧:");
for(i = 0; i < f.length; i++) {
Class type = f[i].getType(); // 変数の型を取得
System.out.println(type.getName() + " "+ f[i].getName() ); // 変数の型 と 変数名
}

System.out.println();

/* メソッド一覧を出力 */
System.out.println("メソッド一覧:");
for(i = 0; i < m.length; i++) {
System.out.println(m[i].getName()); // メソッド名出力

Class ret_type = m[i].getReturnType(); // メソッドの戻り値の型
System.out.println("\t戻り値の型:"+ret_type.getName()); // 戻り値の型

System.out.println("\t引数の型一覧:");
Class paramsType[] = m[i].getParameterTypes(); // 引数の型一覧を取得
for(j = 0; j < paramsType.length; j++) {
System.out.println("\t"+paramsType[j].getName()); // 引数の型リストを出力します
}
}

System.out.println();

/* コンストラクタ一覧です */
System.out.println("コンストラクタ一覧:");
for(i = 0; i < cst.length; i++) {
System.out.println(cst[i].getName()); // コンストラクタ名(クラス名)表示

System.out.println("\t引数の型一覧:");
Class paramsType[] = cst[i].getParameterTypes(); // 引数の型一覧を取得
for(j = 0; j < paramsType.length; j++) {
System.out.println("\t"+paramsType[j].getName()); // 引数の型リストを出力します
}
}

System.out.println();

/* これを使って実際にオブジェクトを作りませう */
Object[] param = new Object[0]; // コンストラクタに渡す 引数リスト。引数の個数0
Object obj = cst[0].newInstance(param); // コンストラクタ一覧の

// TestClassのProcメソッドへ渡す引数の準備
Object[] param2 = new Object[2]; // 引数一覧
param2[0] = new Integer(3); // int型は Integerにする必要あり
param2[1] = "OK!"; // String型は文字列直でおけます。

m[0].invoke(obj, param2); // ↓のコード同じ(笑) tは TestClass型です。

//TestClass t = new TestClass();
//t.Proc(3,"OK!");
}
}




ダイナミックロードはもっと簡単にできないのか?
ロードするクラスに実装するインターフェイスを決めていれば,結構簡単に出来ます。
もちろんインターフェイスにしかないメソッドしか呼び出せないが,
十分すぎるでしょう。

import java.lang.reflect.Constructor;

// このインターフェイスを実装するクラスに限定
interface ITestIfc
{
void Proc(int a, String s);
}

// 実験用のクラス。このクラスをロードしています。
class TestClass2 implements ITestIfc
{
public int num;
public double gas;
public static int static_val;
public void Proc(int a, String s)
{
System.out.println("a = "+ a + " s = "+s);
}
public static void TestProc2(double a, String s)
{
}
}

class DLoadClass2
{
public static void main(String[] args) throws Exception
{
Class c = Class.forName("TestClass2"); // 上記のclass TestClass のクラス情報ロード
Constructor cst[] = c.getDeclaredConstructors(); // コンストラクタ一覧取得。getConstructors じゃないのは,デフォルトのコンストラクタも取りたいので

Object[] param = new Object[0]; // コンストラクタに渡す 引数リスト。引数の個数0
ITestIfc i = (ITestIfc) cst[0].newInstance(param); // オブジェクト作成。引数なしコンストラクタ呼び出し
i.Proc(3,"OK!");
}
}




もっと自分でロードする部分を用意しいたい場合は
ClassLoader
を継承したオリジナルのクラスを作る。
このClassLoaderクラスは,Classのオブジェクトを作成するクラスで
外部からクラスをロードして独自のClassをロードできる。

継承して,オーバーライドしなければならないメソッドは以下の2つ。
以下が基本形態だ。

import java.util.*;
import java.io.*;

class OrgClassLoader extends ClassLoader {

public Class loadClass(String name) throws ClassNotFoundException {
return loadClass(name, true);
}

protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class cl = null ;
try {
return findSystemClass(name); // ここで独自の読み込みを返せば,ダイナミックロードは完璧!
} catch( Exception e){
System.err.println(e);
}
return cl;
}

}

さらに細かく設定するには,

Class defineClass(String name, byte[] data, int off, int len)
を使う。
name : クラス名
data : コード本体が入った配列
off : dataの配列の何番目から使うか? data全体にするには 0
len : data のoffから何個がクラスか data全体にするには data.length

で設定して,戻り値をreturn すればいい
———-

参照:wiki内
http://ja.wikipedia.org/wiki/Java_Platform,_Standard_Edition#java.lang.reflect

comments

コメントを残す