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

喜欢而已 提交于 2019-12-05 09:38:51

This is a bug in the JVM (bug 4819108)

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

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.

The problem here is that in the code that uses reflection, you are reflecting the class rather than the interface.

Under non-reflection circumstances, the listener would not be considered to be of type presenter.Presenter$1. You would be using it through a ModelChangedHandler reference. ModelChangedHandler is a public type and it has a public method, and that polymorphic access would be allowed.

But because you are using getClass(), you are getting the actual implementing class. Normally, this class is not accessible at all. Local and anonymous classes are not top-level and not member classes. Therefore "access" is not defined for them.

In fact, the real bug here is the fact that the reflection mechanism views the "no access modifiers" as "default access" which is "package private". So it allows this operation when the types are in the same package. IMO, it should have reported IllegalAccessException even when they are in the same package, as there is no access to the given class from where you are calling it, and the access restriction should explicitly be lifted with method.setAccessible(true).

So what would be the more correct way of doing this? You should access it using the interface Class object.

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 the constructor a Class object for the required interface, as well as the method name. We create the Method object in the constructor. It is a reflection of the method in the interface itself. Not the class.

In dispatch, when we invoke the method, we apply the interface's method to the given listener. This is reflection 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 the Model, we use the interface's class literal - which 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 the try-catch.

This time - no access problems. The method is invoked polymorphically, and is perfectly accessible!

This is a really bad idea to use reflection in that case. Just let your dispatcher call the required method. If you need several dispatchers to call different methods, just subclass them.

Java is missing closures but help is on the way!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!