Проблема здесь в том, что в коде, который использует отражение, вы отражаете класс, а не интерфейс.
В условиях отсутствия отражения listener
не считается типом presenter.Presenter$1
. Вы будете использовать его через ссылку ModelChangedHandler
. ModelChangedHandler
- это открытый тип, и у него есть открытый метод, и этот полиморфный доступ будет разрешен.
Но поскольку вы используете getClass()
, вы получаете реальный реализующий класс. Обычно этот класс вообще недоступен. Локальные и анонимные классы не являются классами верхнего уровня и не являются членами. Поэтому для них «доступ» не определен.
Фактически, настоящая ошибка здесь заключается в том, что механизм отражения рассматривает «модификаторы отсутствия доступа» как «доступ по умолчанию», который является «закрытым пакетом». Так что это разрешает эту операцию, когда типы находятся в одном пакете. ИМО, он должен был сообщить IllegalAccessException
, даже если они находятся в одном пакете, так как нет доступа к данному классу, откуда вы его вызываете, и ограничение доступа должно быть явно снято с method.setAccessible(true)
.
Так что было бы более правильным способом сделать это? Вы должны получить к нему доступ, используя интерфейс Class
объект.
package util;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class EventDispatcher<T> {
List<T> listeners;
Method method;
public EventDispatcher(Class<? extends T> cls, String methodName) throws NoSuchMethodException, SecurityException {
listeners = new ArrayList<T>();
this.method = cls.getMethod(methodName);
}
public void add(T listener) {
listeners.add(listener);
}
public void dispatch() {
for (T listener : listeners) {
try {
method.invoke(listener);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}
В этой версии мы передаем конструктору объект Class для требуемого интерфейса, а также имя метода. Мы создаем объект Method
в конструкторе. Это отражение метода в самом интерфейсе . Не класс.
В dispatch
, когда мы вызываем метод, мы применяем метод интерфейса к данному слушателю. Это отражение в сочетании с полиморфизмом.
package model;
import util.EventDispatcher;
public class Model {
private EventDispatcher<ModelChangedHandler> dispatcher;
public Model() throws NoSuchMethodException, SecurityException {
dispatcher = new EventDispatcher<ModelChangedHandler>(ModelChangedHandler.class, "modelChanged");
}
public void whenModelChange(ModelChangedHandler handler) {
dispatcher.add(handler);
}
public void change() {
dispatcher.dispatch();
}
}
Так что здесь, в Model
, мы используем литерал класса интерфейса - который мы знаем, потому что именно здесь мы решаем, какой интерфейс использовать.
package main;
import model.Model;
import presenter.Presenter;
public class Main {
public static void main(String[] args) {
Model model;
try {
model = new Model();
Presenter presenter = new Presenter(model);
model.change();
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
}
Единственное изменение здесь - это try-catch.
На этот раз - нет проблем с доступом. Метод вызывается полиморфно и совершенно доступен!