JavaのリフレクションAPIでメソッド取得、メソッド実行
JavaのリフレクションAPIでメソッドの取得、メソッドの実行を試してみたメモです。
JavaのリフレクションってforName("ClassName")くらいしか使ったことなかったので、
これを機にもう少しちゃんと勉強してみようと思いました。
java.lang.reflect.Method
JavaのリフレクションAPIを利用することで、メタデータにアクセスできます。
java.lang.reflect.Methodインターフェースを利用してメソッドの取得、メソッドの実行を試してみたいと思います。
テスト用クラス
Mavenプロジェクトを作成し、pom.xmlはjunitとhamcrestだけdependencyに追加しました。
pom.xml
<dependencies> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>1.3</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit-dep</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies>
テスト対象のクラスは下記のようなクラスを作りました。
package reflect; public class Circle { private int radius; public Circle() { this(10); } public Circle(int radius) { this.radius = radius; } public int area() { return area(radius); } public int area(int r) { return (int) (r * r * Math.PI); } public int publicMethod() { return 1; } protected int protectedMethod() { return 2; } int defaultMethod() { return 3; } private int privateMethod() { return 4; } public static int staticMethod() { return 11; } @Override public String toString() { return "radius : " + radius; } }
public、protected、修飾子なし、private
のアクセス修飾子でそれぞれメソッドを作成しました。
あと、toString()のみオーバーライドしています。
Methodオブジェクト
Methodオブジェクトを取得するためのメソッドは下記です。
すべてClassクラスのメソッドです。
メソッド | 戻り値 | 取得可能なアクセス修飾子 | 取得可能なクラス |
---|---|---|---|
getMethod | Method | public | 自クラスとスーパークラス |
getMethods | Method[] | public | 自クラスとスーパークラス |
getDeclaredMethod | Method | すべて | 自クラス |
getDeclaredMethods | Method[] | すべて | 自クラス |
getMethod()
getMethod()では、自クラスとスーパークラスのメソッドが取得できます。
取得できるのはpublicメソッドのみです。
引数にはメソッド名の文字列を指定します。
Method method = Circle.class.getMethod("area");
同じメソッドでオーバーロードしている場合は、第2引数以降に引数の型を指定します。
// 引数なし Method method = Circle.class.getMethod("area"); // 引数あり Method method = Circle.class.getMethod("area", int.class);
スーパークラスのメソッドも取得できるので、Circleクラスでは定義していないhashcode()が取得できます。
// Objectクラスで定義されたメソッド Method method = Circle.class.getMethod("hashCode");
テストコードはこんな感じで書きました。
public static class getMethodの確認 { @Test public void getMethodでareaメソッドのMethodオブジェクトが取得できること() throws Exception { Method method = Circle.class.getMethod("area"); assertThat(method.toString(), is("public int reflect.Circle.area()")); } @Test public void getMethodで引数ありのareaメソッドのMethodオブジェクトが取得できること() throws Exception { Method method = Circle.class.getMethod("area", int.class); assertThat(method.toString(), is("public int reflect.Circle.area(int)")); } @Test public void getMethodでpublicメソッドが取得できること() throws Exception { Method pub = Circle.class.getMethod("publicMethod"); assertThat(pub.toString(), is("public int reflect.Circle.publicMethod()")); } @Test(expected = NoSuchMethodException.class) public void getMethodでprotectedメソッドが取得できないこと() throws Exception { Method pro = Circle.class.getMethod("protectedMethod"); assertThat(pro.toString(), is("protected int reflect.Circle.protectedMethod()")); } @Test(expected = NoSuchMethodException.class) public void getMethodでdefaultメソッドが取得できないこと() throws Exception { Method def = Circle.class.getMethod("defaultMethod"); assertThat(def.toString(), is("int reflect.Circle.defaultMethod()")); } @Test(expected = NoSuchMethodException.class) public void getMethodでprivateメソッドが取得できないこと() throws Exception { Method pri = Circle.class.getMethod("privateMethod"); assertThat(pri.toString(), is("private int reflect.Circle.privateMethod()")); } @Test public void getMethodでhashCodeメソッドが取得できること() throws Exception { Method method = Circle.class.getMethod("hashCode"); assertThat(method.toString(), is("public native int java.lang.Object.hashCode()")); } }
getMethods()
getMethods()では、自クラスとスーパークラスのメソッドがMethodクラスの配列で取得できます。
取得できるのはpublicメソッドのみです。
スーパークラスのメソッドも取得できるので、Objectクラスで定義されているpublicメソッドも取得されます。
テストコードはこんな感じで書きました。
public static class getMethodsの確認 { @Test public void getMethodsでCircleクラスとスーパークラスのメソッドが取得できること() throws Exception { Class clazz = Circle.class; Method[] methods = clazz.getMethods(); Method[] expected = { clazz.getMethod("area"), clazz.getMethod("area", int.class), clazz.getMethod("publicMethod"), clazz.getMethod("staticMethod"), clazz.getMethod("toString"), clazz.getMethod("wait"), clazz.getMethod("wait", long.class), clazz.getMethod("wait", long.class, int.class), clazz.getMethod("equals", Object.class), clazz.getMethod("hashCode"), clazz.getMethod("getClass"), clazz.getMethod("notify"), clazz.getMethod("notifyAll") }; assertThat(methods, is(arrayContainingInAnyOrder(expected))); } }
getDeclaredMethod()
getDeclaredMethod()では、自クラスで定義されたメソッドのみ取得できます。
getMethod()と異なり、すべてのアクセス修飾子(public、protected、デフォルト、private)のメソッドが取得できます。
// publicメソッド Method pub = Circle.class.getDeclaredMethod("publicMethod"); // protectedメソッド Method pro = Circle.class.getDeclaredMethod("protectedMethod"); // defaultメソッド Method def = Circle.class.getDeclaredMethod("defaultMethod"); // privateメソッド Method pri = Circle.class.getDeclaredMethod("privateMethod");
スーパークラスのメソッドは取得できませんが、オーバーライドしていれば取得可能です。
// toString()はオーバーライドしているため取得可能 Method toStr = Circle.class.getDeclaredMethod("toString");
テストコードはこんな感じで書きました。
public static class getDeclaredMethodの確認 { @Test public void getDeclaredMethodでareaメソッドが取得できること() throws Exception { Method method = Circle.class.getDeclaredMethod("area"); assertThat(method.toString(), is("public int reflect.Circle.area()")); } @Test public void getDeclaredMethodでpublicメソッドが取得できること() throws Exception { Method pub = Circle.class.getDeclaredMethod("publicMethod"); assertThat(pub.toString(), is("public int reflect.Circle.publicMethod()")); } @Test public void getDeclaredMethodでprotectedメソッドが取得できること() throws Exception { Method pro = Circle.class.getDeclaredMethod("protectedMethod"); assertThat(pro.toString(), is("protected int reflect.Circle.protectedMethod()")); } @Test public void getDeclaredMethodでdefaultメソッドが取得できること() throws Exception { Method def = Circle.class.getDeclaredMethod("defaultMethod"); assertThat(def.toString(), is("int reflect.Circle.defaultMethod()")); } @Test public void getDeclaredMethodでprivateメソッドが取得できること() throws Exception { Method pri = Circle.class.getDeclaredMethod("privateMethod"); assertThat(pri.toString(), is("private int reflect.Circle.privateMethod()")); } @Test public void getDeclaredMethodでオーバーライドメソッドが取得できること() throws Exception { Method toStr = Circle.class.getDeclaredMethod("toString"); assertThat(toStr.toString(), is("public java.lang.String reflect.Circle.toString()")); } @Test(expected = NoSuchMethodException.class) public void getDeclaredMethodでhashCodeメソッドが取得できないこと() throws Exception { Method method = Circle.class.getDeclaredMethod("hashCode"); assertThat(method.toString(), is("public native int java.lang.Object.hashCode()")); } }
getDeclaredMethods()
getDeclaredMethods()では、自クラスで定義されたメソッドがMethodクラスの配列で取得できます。
getMethod()と異なり、すべてのアクセス修飾子(public、protected、デフォルト、private)のメソッドが取得できます。
スーパークラスのメソッドは取得できませんが、オーバーライドしていれば取得可能です。
toString()はオーバーライドしているため取得されます。
テストコードはこんな感じ。
public static class getDeclaredMethodsの確認 { @Test public void getDeclaredMethodsでCircleクラスのメソッドが取得できること() throws Exception { Class clazz = Circle.class; Method[] methods = clazz.getDeclaredMethods(); Method[] expected = { clazz.getDeclaredMethod("area"), clazz.getDeclaredMethod("area", int.class), clazz.getDeclaredMethod("publicMethod"), clazz.getDeclaredMethod("protectedMethod"), clazz.getDeclaredMethod("defaultMethod"), clazz.getDeclaredMethod("privateMethod"), clazz.getDeclaredMethod("staticMethod"), clazz.getDeclaredMethod("toString") }; assertThat(methods, is(arrayContainingInAnyOrder(expected))); } }
Methodクラス
メソッド情報取得
Methodクラスのメソッドを利用してメソッドの情報が取得できます。
メソッド名、メソッドが定義されたクラス、戻り値の型、引数の型を取得してみます。
Method method = Circle.class.getMethod("area"); // メソッド名取得 String methodName = method.getName(); // メソッドが定義されたクラス取得 Class clazz = method.getDeclaringClass(); // メソッドの引数の型を取得 Class<?>[] argClazz = method.getParameterTypes(); // メソッドの戻り値の型を取得 Class retClazz = method.getReturnType();
テストコードはこんな感じ。
public static class Methodクラスでメソッド情報取得 { @Test public void メソッド名を取得() throws Exception { Method method = Circle.class.getMethod("area"); String methodName = method.getName(); assertThat(methodName, is("area")); } @Test public void メソッドが定義されたクラスを取得() throws Exception { Method method = Circle.class.getMethod("area"); Class clazz = method.getDeclaringClass(); assertThat(clazz, is(equalTo(Circle.class))); } @Test public void メソッドの引数の型を取得() throws Exception { Method method = Circle.class.getMethod("area", int.class); Class<?>[] argClazz = method.getParameterTypes(); assertThat(argClazz, is(arrayContainingInAnyOrder(int.class))); } @Test public void メソッドの戻り値の型を取得() throws Exception { Method method = Circle.class.getMethod("area"); Class retClazz = method.getReturnType(); assertThat(retClazz, is(equalTo(int.class))); } }
invoke()でメソッドコール
invoke()メソッドでメソッドをコールできます。
引数にはオブジェクトを指定し、そのオブジェクトに対してメソッドが実行されます。
// CircleクラスのpublicMethod()を実行 Circle circle = Circle.class.newInstance(); Method method = Circle.class.getMethod("publicMethod"); method.invoke(circle);
戻り値として、コールしたメソッドの戻り値が戻ります。
int ret = (int) method.invoke(circle);
引数を取るメソッドをコールする場合、第2引数以降に指定します。
// Circleクラスのarea(int)メソッドに引数3を与えて実行 method.invoke(circle, 3);
staticメソッドをコールする場合、第1引数は無視されます。
nullを指定して実行できます。
// staticメソッドをコールする
method.invoke(nullValue());
テストコードはこんな感じ。
public static class invokeでメソッドコール { @Test public void invokeで引数なしのメソッドを実行() throws Exception { Circle circle = Circle.class.newInstance(); Method method = Circle.class.getMethod("publicMethod"); int ret = (int) method.invoke(circle); assertThat(ret, is(1)); } @Test public void invokeで引数ありのメソッドを実行() throws Exception { Circle circle = Circle.class.newInstance(); Method method = Circle.class.getMethod("area", int.class); int ret = (int) method.invoke(circle, 3); assertThat(ret, is(28)); } @Test public void invokeでstaticメソッドを実行() throws Exception { Method method = Circle.class.getMethod("staticMethod"); int ret = (int) method.invoke(nullValue()); assertThat(ret, is(11)); } }
テストコード
今回のテストコードの全体です。
CircleTest.java
package reflect; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; import java.lang.reflect.Method; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.junit.Assert.*; /** * Created by pppurple on 2016/07/18. */ @RunWith(Enclosed.class) public class CircleTest { public static class forNameの確認 { @Test public void Circleクラスのインスタンスが取得できること() throws Exception { Class<?> clazz = Class.forName("reflect.Circle"); Circle circle = (Circle) clazz.newInstance(); assertThat(circle, is(instanceOf(Circle.class))); } } public static class getMethodの確認 { @Test public void getMethodでareaメソッドのMethodオブジェクトが取得できること() throws Exception { Method method = Circle.class.getMethod("area"); assertThat(method.toString(), is("public int reflect.Circle.area()")); } @Test public void getMethodで引数ありのareaメソッドのMethodオブジェクトが取得できること() throws Exception { Method method = Circle.class.getMethod("area", int.class); assertThat(method.toString(), is("public int reflect.Circle.area(int)")); } @Test public void getMethodでpublicメソッドが取得できること() throws Exception { Method pub = Circle.class.getMethod("publicMethod"); assertThat(pub.toString(), is("public int reflect.Circle.publicMethod()")); } @Test(expected = NoSuchMethodException.class) public void getMethodでprotectedメソッドが取得できないこと() throws Exception { Method pro = Circle.class.getMethod("protectedMethod"); assertThat(pro.toString(), is("protected int reflect.Circle.protectedMethod()")); } @Test(expected = NoSuchMethodException.class) public void getMethodでdefaultメソッドが取得できないこと() throws Exception { Method def = Circle.class.getMethod("defaultMethod"); assertThat(def.toString(), is("int reflect.Circle.defaultMethod()")); } @Test(expected = NoSuchMethodException.class) public void getMethodでprivateメソッドが取得できないこと() throws Exception { Method pri = Circle.class.getMethod("privateMethod"); assertThat(pri.toString(), is("private int reflect.Circle.privateMethod()")); } @Test public void getMethodでhashCodeメソッドが取得できること() throws Exception { Method method = Circle.class.getMethod("hashCode"); assertThat(method.toString(), is("public native int java.lang.Object.hashCode()")); } } public static class getMethodsの確認 { @Test public void getMethodsでCircleクラスとスーパークラスのメソッドが取得できること() throws Exception { Class clazz = Circle.class; Method[] methods = clazz.getMethods(); Method[] expected = { clazz.getMethod("area"), clazz.getMethod("area", int.class), clazz.getMethod("publicMethod"), clazz.getMethod("staticMethod"), clazz.getMethod("toString"), clazz.getMethod("wait"), clazz.getMethod("wait", long.class), clazz.getMethod("wait", long.class, int.class), clazz.getMethod("equals", Object.class), clazz.getMethod("hashCode"), clazz.getMethod("getClass"), clazz.getMethod("notify"), clazz.getMethod("notifyAll") }; assertThat(methods, is(arrayContainingInAnyOrder(expected))); } } public static class getDeclaredMethodの確認 { @Test public void getDeclaredMethodでareaメソッドが取得できること() throws Exception { Method method = Circle.class.getDeclaredMethod("area"); assertThat(method.toString(), is("public int reflect.Circle.area()")); } @Test public void getDeclaredMethodでpublicメソッドが取得できること() throws Exception { Method pub = Circle.class.getDeclaredMethod("publicMethod"); assertThat(pub.toString(), is("public int reflect.Circle.publicMethod()")); } @Test public void getDeclaredMethodでprotectedメソッドが取得できること() throws Exception { Method pro = Circle.class.getDeclaredMethod("protectedMethod"); assertThat(pro.toString(), is("protected int reflect.Circle.protectedMethod()")); } @Test public void getDeclaredMethodでdefaultメソッドが取得できること() throws Exception { Method def = Circle.class.getDeclaredMethod("defaultMethod"); assertThat(def.toString(), is("int reflect.Circle.defaultMethod()")); } @Test public void getDeclaredMethodでprivateメソッドが取得できること() throws Exception { Method pri = Circle.class.getDeclaredMethod("privateMethod"); assertThat(pri.toString(), is("private int reflect.Circle.privateMethod()")); } @Test public void getDeclaredMethodでオーバーライドメソッドが取得できること() throws Exception { Method toStr = Circle.class.getDeclaredMethod("toString"); assertThat(toStr.toString(), is("public java.lang.String reflect.Circle.toString()")); } @Test(expected = NoSuchMethodException.class) public void getDeclaredMethodでhashCodeメソッドが取得できないこと() throws Exception { Method method = Circle.class.getDeclaredMethod("hashCode"); assertThat(method.toString(), is("public native int java.lang.Object.hashCode()")); } } public static class getDeclaredMethodsの確認 { @Test public void getDeclaredMethodsでCircleクラスのメソッドが取得できること() throws Exception { Class clazz = Circle.class; Method[] methods = clazz.getDeclaredMethods(); Method[] expected = { clazz.getDeclaredMethod("area"), clazz.getDeclaredMethod("area", int.class), clazz.getDeclaredMethod("publicMethod"), clazz.getDeclaredMethod("protectedMethod"), clazz.getDeclaredMethod("defaultMethod"), clazz.getDeclaredMethod("privateMethod"), clazz.getDeclaredMethod("staticMethod"), clazz.getDeclaredMethod("toString") }; assertThat(methods, is(arrayContainingInAnyOrder(expected))); } } public static class Methodクラスでメソッド情報取得 { @Test public void メソッド名を取得() throws Exception { Method method = Circle.class.getMethod("area"); String methodName = method.getName(); assertThat(methodName, is("area")); } @Test public void メソッドが定義されたクラスを取得() throws Exception { Method method = Circle.class.getMethod("area"); Class clazz = method.getDeclaringClass(); assertThat(clazz, is(equalTo(Circle.class))); } @Test public void メソッドの引数の型を取得() throws Exception { Method method = Circle.class.getMethod("area", int.class); Class<?>[] argClazz = method.getParameterTypes(); assertThat(argClazz, is(arrayContainingInAnyOrder(int.class))); } @Test public void メソッドの戻り値の型を取得() throws Exception { Method method = Circle.class.getMethod("area"); Class retClazz = method.getReturnType(); assertThat(retClazz, is(equalTo(int.class))); } } public static class invokeでメソッドコール { @Test public void invokeで引数なしのメソッドを実行() throws Exception { Circle circle = Circle.class.newInstance(); Method method = Circle.class.getMethod("publicMethod"); int ret = (int) method.invoke(circle); assertThat(ret, is(1)); } @Test public void invokeで引数ありのメソッドを実行() throws Exception { Circle circle = Circle.class.newInstance(); Method method = Circle.class.getMethod("area", int.class); int ret = (int) method.invoke(circle, 3); assertThat(ret, is(28)); } @Test public void invokeでstaticメソッドを実行() throws Exception { Method method = Circle.class.getMethod("staticMethod"); int ret = (int) method.invoke(nullValue()); assertThat(ret, is(11)); } } }
ソースはあげておきました。
https://github.com/pppurple/java_examples/tree/master/reflection_methodgithub.com
終わり。