使用Java反射调用匿名类的访问例外
-
27-09-2019 - |
题
我正在尝试使用事件调度程序来允许模型在更改时通知订阅的侦听器。事件调度程序在调度期间接收一个处理程序类和一个方法名称。主持人订阅模型更改,并提供处理程序实现,以进行更改。
这是代码(对不起,它有点长)。
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());
}
}
}
}
模型:
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();
}
主持人:
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");
}
});
}
}
主要的:
package main;
public class Main {
public static void main(String[] args) {
Model model = new Model();
Presenter presenter = new Presenter(model);
model.change();
}
}
现在,我希望获得“模型更改”消息。但是,我得到了一个java.lang.lang.illegalaccessexception:class utils.eventdispatcher无法访问classersinger.presenter $ 1的成员,带有修饰符“ public”。
我知道要怪的是我在主持人中创建的匿名课程,但是我不知道如何使其比目前更“公开”。如果我用名称的嵌套类代替它,它似乎可以工作。如果演示者和EventDispatcher在同一软件包中,它也可以工作,但我不允许(不同软件包中的几个演示者应使用EventDisPatcher)
有任何想法吗?
解决方案
这是JVM中的错误(错误4819108)
解决方法是打电话 method.setAccessible(true)
在打电话之前 method.invoke(listener)
其他提示
我的猜测是一个匿名课程总是 private
, ,但我没有在Java语言规范中找到有关此的清晰陈述(我在§15.9.5中查看)
在Java中,如果无法访问类型,则其成员也不是。
如果您喜欢黑魔法,可以使用 method.setAccessible(true)
. 。替代性,您可以要求活动处理程序命名类别,或者在可访问类型中声明所讨论的方法。
这里的问题在于,在使用反射的代码中,您反映了类而不是接口。
在非反射情况下, listener
不会被认为是类型 presenter.Presenter$1
. 。您会通过 ModelChangedHandler
参考。 ModelChangedHandler
是一种公共类型,它具有公共方法,并且可以允许多态性访问。
但是因为你正在使用 getClass()
, ,您正在获得实际实施类。通常,这个课程根本无法访问。本地和匿名课程不是顶级的,也不是成员类。因此,“访问”并未为他们定义。
实际上,这里真正的错误是,反射机制将“无访问修饰符”视为“默认访问”,即“ package私有”。因此,当类型在同一软件包中时,它允许此操作。 IMO,应该报告 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());
}
}
}
}
在此版本中,我们将构造函数传递给所需界面的类对象以及方法名称。我们创建 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();
}
}
}
这里唯一的更改是TryCatch。
这次 - 没有访问问题。该方法是通过多态调用的,并且完全可以访问!
在这种情况下,使用反射是一个非常糟糕的主意。只需让您的调度员调用所需的方法即可。如果您需要几个调度器来调用不同的方法,请将其子类插入。
Java缺少关闭,但帮助!