Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

access exception when invoking method of an anonymous class using java reflection

I'm trying to use an event dispatcher to allow a model to notify subscribed listeners when it changes. the event dispatcher receives a handler class and a method name to call during dispatch. the presenter subscribes to the model changes and provide a Handler implementation to be called on changes.

Here's the code (I'm sorry it's a bit long).

EventDispacther:

package utils;

public class EventDispatcher<T> {
    List<T> listeners;
    private String methodName;

    public EventDispatcher(String methodName) {
        listeners = new ArrayList<T>();
        this.methodName = methodName;
    }

    public void add(T listener) {
        listeners.add(listener);
    }

    public void dispatch() {
        for (T listener : listeners) {
            try {
                Method method = listener.getClass().getMethod(methodName);
                method.invoke(listener);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    }
}

Model:

package model;

public class Model {
    private EventDispatcher<ModelChangedHandler> dispatcher;

    public Model() {
        dispatcher = new EventDispatcher<ModelChangedHandler>("modelChanged");
    }

    public void whenModelChange(ModelChangedHandler handler) {
        dispatcher.add(handler);
    }

    public void change() {
        dispatcher.dispatch();
    }
}

ModelChangedHandler:

package model;

public interface ModelChangedHandler {
    void modelChanged();
}

Presenter:

package presenter;

public class Presenter {

    private final Model model;

    public Presenter(Model model) {
        this.model = model;
        this.model.whenModelChange(new ModelChangedHandler() {

            @Override
            public void modelChanged() {
                System.out.println("model changed");
            }
        });
    }
}

Main:

package main;

public class Main {
    public static void main(String[] args) {
        Model model = new Model();
        Presenter presenter = new Presenter(model);
        model.change();
    }
}

Now, I expect to get the "model changed" message. However, I'm getting an java.lang.IllegalAccessException: Class utils.EventDispatcher can not access a member of class presenter.Presenter$1 with modifiers "public".

I understand that the class to blame is the anonymous class i created inside the presenter, however I don't know how to make it any more 'public' than it currently is. If i replace it with a named nested class it seem to work. It also works if the Presenter and the EventDispatcher are in the same package, but I can't allow that (several presenters in different packages should use the EventDispatcher)

any ideas?

like image 393
Asaf David Avatar asked Apr 17 '10 18:04

Asaf David


2 Answers

This is a bug in the JVM (bug 4819108)

The workaround is to call method.setAccessible(true) before the call to method.invoke(listener)

like image 180
Rob Heiser Avatar answered Sep 25 '22 00:09

Rob Heiser


My guess is that an anonymous class is always private, but I didn't find a clear statement about this in the Java Language Specification (I looked in §15.9.5)

In Java, if a type is not accessible, neither are its members.

If you like black magic, you can disable access checking using method.setAccessible(true). Alternativly, you could require your event handlers to be named classes, or the method in question being declared in accessible types.

like image 29
meriton Avatar answered Sep 27 '22 00:09

meriton