The problem is that in code that uses reflection, you are reflecting a class, not an interface.
In the absence of reflection, the listener will not be considered the type presenter.Presenter$1 . You would use it using the ModelChangedHandler link. ModelChangedHandler is a public type and has a public method and polymorphic access is allowed.
But since you use getClass() , you get the actual implementation class. Usually this class is not available at all. Local and anonymous classes are not top-level classes, not members. Therefore, “access” is not defined for them.
In fact, the real mistake is that the reflection mechanism treats “without access modifiers” as “default access”, which is a “private package”. Thus, it allows this operation when the types are in the same package. IMO, it should have reported an IllegalAccessException , even if they are in the same package, since there is no access to this class from where you are calling it, and access restriction should be explicitly removed using method.setAccessible(true) .
So, what would be a more proper way to do this? You must access it using the Class interface.
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()); } } } }
In this version, we pass to the constructor a class object for the required interface, as well as the name of the method. We create a Method object in the constructor. This is a reflection of the method in the interface itself. Not a class.
In dispatch , when we call a method, we apply an interface method to this listener. This reflection is combined with polymorphism.
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(); } }
So, here in Model , we use an interface class literal that we know, because it is here that we decide which interface to use.
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(); } } }
The only change here is a try-catch attempt.
This time, access problems. The method is called polymorphically and is perfectly accessible!