按类动态检索侦听器实例

时间:2014-06-10 22:06:01

标签: java generics reflection

我已经建立了一个系统,用于通过将特定类传递给getter方法来检索侦听器类。例如,你可以像这样调用它:

GlobalListener lis = /*Manager instance*/.getListener(GlobalListener.class);

/*Manager instance*/是我的ListenerManager类的实例,其中包含以下缩写内容:

public class ListenerManager {

    private final Project project;
    private final Map<String, SubListener> listeners = new HashMap<>();

    /**
     * {@link ListenerManager} constructor
     * 
     * @since 1.0.0
     * @version 1.0.0
     * 
     * @param plugin The main {@link Project} instance
     */
    public ListenerManager(Project project) {
        this.project = project;
        this.registerDefaults();
    }

    private void registerDefaults() {
        SubListener[] list = new SubListener[] {
            new GlobalListener(this.project),
            new ItemListener(this.project)
        };

        for (SubListener lis : list) {
            this.registerListener(lis);
        }
    }

    /**
     * Gets a listener by its string name. Returns null if the listener is
     * disabled or not registered.
     * 
     * @since 1.0.0
     * @version 1.0.0
     * 
     * @param <T> The SubListener class
     * @param listener An instance of the class type extending
     *                 {@link SubListener} to retrieve
     * @return The listener class, null if disabled or not registered
     */
    public <T extends SubListener> T getListener(Class<T> listener) {
        return listener.cast(this.listeners.get(this.getListenerName(listener)));
    }

    /**
     * Retrieves a listener's name
     */
    protected String getListenerName(Class<? extends SubListener> listener) {
        try {
            Method m = listener.getDeclaredMethod("getName");
            return (String) m.invoke(null);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            this.project.getLogger().log(Level.SEVERE, "Error reflecting listener field for name!", ex);
        }
        return null;
    }

    /**
     * Registers a listener through the plugin manager and {@link ListenerManager}
     * 
     * @since 1.0.0
     * @version 1.0.0
     * 
     * @param <T> The {@link SubListener} to register
     * @param listener The listener to register
     * @throws ListenerReregisterException Attempted to register a Listener under a similar key
     */
    public <T extends SubListener> void registerListener(T listener) throws ListenerReregisterException {
        String name = listener.getName();
        if (!this.listeners.containsKey(name)) {
            this.listeners.put(name, listener);
            this.project.getServer().getPluginManager().registerEvents(listener, this.plugin);
        } else {
            throw new ListenerReregisterException("Listener Map already contains key: " + name);
        }
    }

}

如图所示,它通过SubListener反映方法getName的{​​{1}}类,这是Method#invoke(null)类的最终内容:

SubListener

然而,在我最近的测试中,这个系统似乎不再适用了,过去它的工作完全正常。现在,行public class SubListener implements Listener { private final Project project; public SubListener(Project project) { this.project = project; } public final String getName() { return this.getClass().getName() + "@" + this.getClass().getPackage().getName(); } } 会抛出m.invoke(null)。我认为这是因为我将NoSuchMethodException传递给了null参数,但是它如何在事先完美地运行,但现在正在破坏?

请注意,我无法选择将#invoke方法设为静态,因为它可以通过获取当前的类来实现。通过getName

命名

2 个答案:

答案 0 :(得分:1)

您正在调用Method#invoke(Object, Object...)方法,该方法接受访问者实例作为其第一个参数。

如果方法标记为null,那么将static作为第一个arg传递将非常正确,但您的方法是一个实例方法,正如您所提到的那样,并且您已发布的代码已经证明了这一点。 (我假设你没有在每个监听器上使用静态方法getName()

我无法完全回答为什么它之前有效 - 我唯一的猜测是你可能没有调用方法,因为我看不到任何方式可以在没有实例的情况下调用实例方法。< / p>

问题的一个可能解决方案是使用getClass().getName()作为侦听器的键,因为在保存实例时以及在获取实例时都有类。此外,您的getName()方法没有提供比我的方法更独特的密钥 - 它只是以另一种格式返回它。

旁注:Class#getName()已经返回了类的完全限定名称,无需返回包名称 - 您的方法会产生my.package.Class@my.package之类的内容。

答案 1 :(得分:1)

之前应该没有用,因为Method#invoke(null)调用方法就好像它是静态的,这意味着没有对实例的引用。除非您有静态getName(),否则这不应该有效。

你要么必须

A)让一个Listener实例代替null(Method#invoke(instance)

B)getName()是一个静态方法,取决于项目是什么,可能不起作用