IllegalAccessException при использовании динамических методов вызова
Я пытаюсь использовать Reflection в Java, но я получаю странную ошибку. Каковы возможные проблемы, когда я получаю сообщение об ошибке:
java.lang.IllegalAccessException: Class com.myapp.core.utils.EventDispatcher can not access a member of class appApp$1 with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
Я просто пытаюсь создать свой собственный класс EventDispatcher и внутри него, часть, в которой я использовал отражение, которая также является строкой кода, которая вызывает проблему:
public void dispatchEvent(Event e, String callMethName) {
IEventListener list = ((IEventListener)listeners[i]);
list.getClass().getMethod(callMethName, Event.class).invoke(list, e);
}
В моем основном классе у меня есть что-то, что вызывает addListener, который просто добавит слушателя в список класса EventDispatcher следующим образом:
try {
obj.addListener("onTestHandler", new MyTestEventListener(){
@Override
public void onTestHandler(Event e) {
System.out.println("hello!");
}
});
} catch (SecurityException e) {
e.printStackTrace();
}
Итак, первый параметр, который говорит "onTestHandler", перейдет в класс EventDispatcher и, в конечном итоге, как часть параметра callMethName в методе dispatchEvent, который будет динамически вызывать метод.
Прохождение методов и все правильно. Это та часть, которая имеет отражение, как-то имеет проблемы. Кажется, он способен найти метод. Но по какой-то причине выдает исключение IllegalAccessException и не может вызвать метод.
Почему это так?
Спасибо.
Ответы
Ответ 1
Я подозреваю, что анонимный класс (appApp$1
), реализующий MyTestEventListener
, имеет видимость пакета и что код отражения находится в другом пакете.
Для этого кода:
package foo.p1;
public class Target {
public static interface Foo {
public void bar();
}
public static Foo newFoo() {
return new Foo() {
@Override
public void bar() {
}
};
}
}
Этот код не сработает, потому что тип среды выполнения, возвращаемый newFoo()
, не является общедоступным классом:
package foo.p2;
import foo.p1.Target;
public class Main {
public static void main(String[] args) throws Exception {
Target.Foo foo = Target.newFoo();
foo.getClass()
.getMethod("bar")
.invoke(foo);
}
}
Это можно преодолеть, установив метод как доступный:
Target.Foo foo = Target.newFoo();
Method m = foo.getClass()
.getMethod("bar");
m.setAccessible(true);
m.invoke(foo);
Или используя этот метод из открытого интерфейса:
Target.Foo foo = Target.newFoo();
Target.Foo.class.getMethod("bar")
.invoke(foo);
Ответ 2
Доступ к анонимным классам очень ограничительный. Вместо простого использования list.getClass()
вы должны попытаться найти не анонимный суперкласс или интерфейс объекта, который имеет нужный вам метод, а затем вызвать метод из этот класс вместо анонимного..
Я также предлагаю вам выполнить этот поиск во время добавления слушателя и сохранения результата. Это довольно дорого, чтобы использовать отражение, поэтому материал будет работать быстрее. Столь же важно, если вы сделаете опечатку в имени метода, вы получите ошибку в момент добавления слушателя, а не в какое-то случайное время в будущем. Нам будет намного легче найти проблему таким образом: )