私はこれに従って Java annotaitons のチュートリアルを実行し、そこに示されているようにTestアノテーションを実装しました。ただし、コードを実行すると、次の出力が得られます。
Java.lang.NullPointerException
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:57)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
at Java.lang.reflect.Method.invoke(Method.Java:616)
at TestAnnotationParser.parse(Demo.Java:24)
at Demo.main(Demo.Java:51)
Passed:0 Fail:1
以下は私のコードです。誰かが私が間違ったことを指摘できますか?
import Java.lang.annotation.ElementType;
import Java.lang.annotation.Retention;
import Java.lang.annotation.RetentionPolicy;
import Java.lang.annotation.Target;
import Java.lang.reflect.Method;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Test {
Class expected();
}
class TestAnnotationParser {
public void parse(Class<?> clazz) throws Exception {
Method[] methods = clazz.getMethods();
int pass = 0;
int fail = 0;
for (Method method : methods) {
if (method.isAnnotationPresent(Test.class)) {
Test test = method.getAnnotation(Test.class);
Class expected = test.expected();
try {
method.invoke(null);
pass++;
} catch (Exception e) {
if (Exception.class != expected) {
e.printStackTrace();
fail++;
} else {
pass++;
}
}
}
}
System.out.println("Passed:" + pass + " Fail:" + fail);
}
}
class MyTest {
@Test(expected = RuntimeException.class)
public void testBlah() {
}
}
public class Demo {
public static void main(String[] args) {
TestAnnotationParser parser = new TestAnnotationParser();
try {
parser.parse(MyTest.class);
} catch (Exception e) {
e.printStackTrace();
}
}
}
invoke
に渡すパラメーターは、メソッドがstatic
でない限り、メソッドが呼び出されるオブジェクトでなければなりません。リフレクションで行ったことはこれと同等です。
MyTest obj = null;
obj.testBlah();
当然、NPEがあります。この問題を解決するには、メソッドを呼び出すオブジェクトを渡すか、メソッドをstatic
にします。
修正する方法の1つを次に示します。
public <T> void parse(Class<T> clazz, T obj) throws Exception {
Method[] methods = clazz.getMethods();
int pass = 0;
int fail = 0;
for (Method method : methods) {
if (method.isAnnotationPresent(Test.class)) {
Test test = method.getAnnotation(Test.class);
Class expected = test.expected();
try {
method.invoke(obj);
pass++;
} catch (Exception e) {
if (Exception.class != expected) {
e.printStackTrace();
fail++;
} else {
pass++;
}
}
}
}
System.out.println("Passed:" + pass + " Fail:" + fail);
}
...
parser.parse(MyTest.class, new MyTest());
Method#invoke はあなたの質問に対する答えを持っています:
public Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
スロー:NullPointerException
-指定されたオブジェクトがnullで、メソッドがインスタンスメソッドの場合。
この問題はここにあります:
method.invoke(null);
このメソッドの最初のパラメーターは、メソッドを呼び出すオブジェクトです。これは、次のような動的(反射)なものです。
Object foo = null;
foo.toString();
もちろん、NullPointerException
はfoo
なので、このコードはnull
を与えると予想します。
問題は、nullターゲットオブジェクトをmethod.invoke(object)
メソッドに渡すことです。ターゲットオブジェクトはnullであってはなりません。それ以外の場合はnullpointerexceptionが期待されます。
Invokeメソッドの使用法は以下のとおりです。
Method.invoke(targetObject, args1, args2, args3...); where args1, args2, args3 etc are argument to the method being invoked.