1. まとめトップ

【Java】リフレクション(インスタンス動的生成)の使い方【HowTo】

インスタンスを動的に生成できたらいいのになとか思ったことありませんか?リフレクションはそんなあなたのお手伝いをいたします。

更新日: 2014年05月27日

3 お気に入り 31179 view
お気に入り追加

■リフレクションってなんぞ?

リフレクションとは、実行時のJavaVMにロードされたクラスにアクセスし、取り扱うことのできるパッケージです。
VM実行時に動的に生成される為、再利用性のあるプログラムを書く場合などに利用されます。
んで、動的に生成?とはどういうことかというと、例えばファイルからパッケージ名とクラス名が書かれた文字列を取り出してそこからクラスのインスタンスを作るなどを行うことができます。
一般的な話ではリフレクションは重いなどといわれますが、この辺りの検証は以下で行います。
リフレクションの主な使い方は大きく分けて以下の種類になります。

・クラス
・メソッド
・フィールド

■サンプル(生成及びアクセス対象)

今回のサンプル用として、以下のBeanを用意しました。 このBeanへインスタンス生成、メソッド実行、フィールドアクセスなどを行います。

package jp.co.abc.sample.sampleapp.reflect;

import java.io.Serializable;

public class TestBean implements Serializable {

private String strPublic;
private String strPrivate;
private int int1;

public TestBean(){super();}

/**
* @return strPublic
*/
public String getStrPublic() {
return strPublic;
}

/**
* @param strPublic セットする strPublic
*/
public void setStrPublic(String str1) {
this.strPublic = str1;
}

/**
* @return strPrivate
*/
private String getStrPrivate() {
return strPrivate;
}

/**
* @param strPrivate セットする strPrivate
*/
private void setStrPrivate(String str2) {
this.strPrivate = str2;
}

/**
* @return int1
*/
public int getInt1() {
return int1;
}

/**
* @param int1 セットする int1
*/
public void setInt1(int int1) {
this.int1 = int1;
}

}

■クラスの自動生成処理

クラスの自動生成を行うにはClass.forNameを使用します。 生成するクラスは上記のTestBeanを生成します。 生成したクラスが正常に動作できることまでを確認します。

package jp.co.abc.sample.sampleapp.reflect;

public class MakeClassSample {

public static void main(String args[]){

try {

//TestBeanクラスをロード
Class clazz = Class.forName("jp.co.abc.sample.sampleapp.reflect.TestBean");
try {

//インスタンスを生成する(TestBean)
Object obj = clazz.newInstance();

//生成したTestBeanのクラスがロードされているか確認する
System.out.println(obj.getClass());

//TestBeanにキャストしてみる
TestBean bean = (TestBean) obj;

//文字列をセットする
bean.setStrPublic("クラス作成");

//セットした文字列を表示
System.out.println("getStrPublic = "+bean.getStrPublic());

} catch (InstantiationException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}

}

}

■結果

class jp.co.abc.sample.sampleapp.reflect.TestBean
getStrPublic = クラス作成

■メソッドの動的生成

メソッドを動的に生成するには、ロードされたClassクラスからgetMethodを行いメソッド情報を取得します。
取得したメソッドからは、シグニチャ、アクセス修飾子、戻り値などが取得できます。
まずは2種類(Public , Private)のメソッドを生成して実行します。

package jp.co.abc.sample.sampleapp.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class MakeMethodSample {

public static void main(String args[]){

try {
Class testBeanClass = Class.forName("jp.co.abc.sample.sampleapp.reflect.TestBean");
//public void setConflictStr(String conflictStr)
Method setStrPublic = testBeanClass.getMethod("setStrPublic", String.class);

Object testBeanInstance = testBeanClass.newInstance();

System.out.println("----getMethod-----");

//取得したメソッドの名前を確認
System.out.println("メソッド名は? : " + setStrPublic.getName());
//メソッドのアクセス修飾子の確認
System.out.println("このメソッドはpublicですか? :"+ Modifier.isPublic(setStrPublic.getModifiers()));
//メソッドの戻り値
System.out.println("戻り値は? : "+setStrPublic.getReturnType().getName());
//Object配列はメソッドの引数になります。引数の数分を配列にセットしてください。
setStrPublic.invoke(testBeanInstance,new Object[]{"reflectionを使用して作ったメソッドにアクセスしてみた(Public)"});

//セットした文字列を表示してみる
System.out.println(((TestBean)testBeanInstance).getStrPublic());

//public void setConflictStr(String conflictStr)
Method setStrPrivate = testBeanClass.getMethod("setStrPrivate", String.class);

} catch (NoSuchMethodException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (SecurityException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (InstantiationException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
}

■結果

----getMethod-----
メソッド名は? : setStrPublic
このメソッドはpublicですか? :true
戻り値は? : void
reflectionを使用して作ったメソッドにアクセスしてみた(Public)
java.lang.NoSuchMethodException: jp.co.abc.sample.sampleapp.reflect.TestBean.setStrPrivate(java.lang.String)
at java.lang.Class.getMethod(Class.java:1665)
at jp.co.abc.sample.sampleapp.reflect.MakeMethodSample.main(MakeMethodSample.java:36)

正常にうまくいったパターンと例外が出てしまったパターンがあります。
例外が出てしまった方はPrivate修飾子が付いているメソッドです。
原因は生成時のメソッドになります。
Publicメソッドを生成した際に使用したClassクラスのメソッドはgetMethodになっています。
このメソッドは使う側から見えるスコープ分(Public)しか生成することができません。

使う側からは見えないスコープ(Protected , Private)のメソッドを確認するにはgetDeclaredMethodを使用します。
エラーが出ているgetMethodをgetDeclaredMethodに変更します。また、privateメソッドの存在を確認することはできるけど
このままでは実行ができません。
実行する為にアクセス権を付与する必要があります。それがsetAccessible(boolean)になります。
これにtrueをセットするとprivateなメソッドを実行することができます。
アクセス権を付与して再度実行してみたいと思います。

package jp.co.abc.sample.sampleapp.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class MakeMethodSample {

public static void main(String args[]){
try {
Class testBeanClass = Class.forName("jp.co.abc.sample.sampleapp.reflect.TestBean");
//public void setConflictStr(String conflictStr)
Method setStrPublic = testBeanClass.getMethod("setStrPublic", String.class);

Object testBeanInstance = testBeanClass.newInstance();

System.out.println("----getMethod-----");

//取得したメソッドの名前を確認
System.out.println("メソッド名は? : " + setStrPublic.getName());
//メソッドのアクセス修飾子の確認
System.out.println("このメソッドはpublicですか? :"+ Modifier.isPublic(setStrPublic.getModifiers()));
//メソッドの戻り値
System.out.println("戻り値は? : "+setStrPublic.getReturnType().getName());
//Object配列はメソッドの引数になります。引数の数分を配列にセットしてください。
setStrPublic.invoke(testBeanInstance,new Object[]{"reflectionを使用して作ったメソッドにアクセスしてみた(Public)"});

//セットした文字列を表示してみる
System.out.println(((TestBean)testBeanInstance).getStrPublic());

System.out.println("-----getDeclareMethod----");
//public void setConflictStr(String conflictStr)
//Method setStrPrivate = testBeanClass.getMethod("setStrPrivate", String.class);

//----private実行用に修正した範囲の開始----

Method setStrPrivate = testBeanClass.getDeclaredMethod("setStrPrivate", String.class);
//取得したメソッドの名前を確認
System.out.println("メソッド名は? : " + setStrPrivate.getName());
//メソッドのアクセス修飾子の確認
System.out.println("このメソッドはpublicですか? :"+ Modifier.isPublic(setStrPrivate.getModifiers()));
//メソッドの戻り値
System.out.println("戻り値は? : "+setStrPrivate.getReturnType().getName());

//privateメソッドなので基本実行できないが、外部からのアクセス権限を付与する
setStrPrivate.setAccessible(true);
setStrPrivate.invoke(testBeanInstance,new Object[]{"reflectionを使用して作ったメソッドにアクセスしてみた(Private)"});

Method getStrPrivate = testBeanClass.getDeclaredMethod("getStrPrivate");

//privateメソッドなので基本実行できないが、外部からのアクセス権限を付与する
getStrPrivate.setAccessible(true);

//セットしたPrivate文字列を表示してみる
System.out.println(getStrPrivate.invoke(testBeanInstance));

//----private実行用に修正した範囲の終了----

} catch (NoSuchMethodException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (SecurityException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (InstantiationException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}

}

}

■改めて実行した結果

----getMethod-----
メソッド名は? : setStrPublic
このメソッドはpublicですか? :true
戻り値は? : void
reflectionを使用して作ったメソッドにアクセスしてみた(Public)
-----getDeclareMethod----
メソッド名は? : setStrPrivate
このメソッドはpublicですか? :false
戻り値は? : void
reflectionを使用して作ったメソッドにアクセスしてみた(Private)

■フィールド

フィールドもメソッドと似ていて、オブジェクトの取り方や名称、アクセス修飾子など取り方はほぼ一緒です。 また、アクセス修飾子がprivateな場合も対応が一緒なので、エラーが出た場合の対応などは省きます。

相違点を挙げるとするならメソッドにはinvoke、getReturnTypeがありFieldにはgetTypeがあるなどです。

package jp.co.abc.sample.sampleapp.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class MakeFieldSample {

public static void main(String args[]){
try {

//TestBeanクラスをロード
Class clazz = Class.forName("jp.co.abc.sample.sampleapp.reflect.TestBean");
try {

//インスタンスを生成する(TestBean)
Object obj = clazz.newInstance();

System.out.println("----Public Field----");

//Publicなフィールドを取得
Field publicField = clazz.getField("strPublic");
//取得したフィールドの名前を確認
System.out.println("フィールド名は? : " + publicField.getName());
//メソッドのアクセス修飾子の確認
System.out.println("このフィールドはpublicですか? :"+ Modifier.isPublic(publicField.getModifiers()));
System.out.println("このフィールドの型は? : "+publicField.getType().getName());
System.out.println("このフィールドの値は? : "+publicField.get(obj));

System.out.println("----Private Field----");

Field privateField = clazz.getDeclaredField("strPrivate");
//取得したメソッドの名前を確認
System.out.println("メソッド名は? : " + privateField.getName());
//メソッドのアクセス修飾子の確認
System.out.println("このメソッドはpublicですか? :"+ Modifier.isPublic(privateField.getModifiers()));
//メソッドの戻り値
System.out.println("このフィールドの型は? : "+privateField.getType().getName());

//privateなのでアクセス権を付与する
privateField.setAccessible(true);

System.out.println("このフィールドの値は? : "+privateField.get(obj));

} catch (InstantiationException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (SecurityException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}

}

■結果

----Public Field----
フィールド名は? : strPublic
このフィールドはpublicですか? :true
このフィールドの型は? : java.lang.String
このフィールドの値は? : strPublicString
----Private Field----
メソッド名は? : strPrivate
このメソッドはpublicですか? :false
このフィールドの型は? : java.lang.String
このフィールドの値は? : strPrivateString

■リフレクションは重い?

1 2





monmosu_kinokoさん

SEなので技術HowToとかNewsとか纏めたいなあって思います(=゚ω゚)