Skip to main content
Updated to fit style of edits.
Source Link

Note: this is the old code, kept for TwoThe's answer. See below for new code.

public class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source;
    Method handler = null;
    Class E;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = E;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + E.getSimpleName() + ") found.");
        }
        this.E = E;
    }

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod)
    {
        this(E, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected,
                                                 //delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. 
                                   //Reference to listener is required to deregister later.
    {
        EventManager.getInstance().addListener(E, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(E, this);
    }
}

New code:

EventManager.java

public final class EventManager //The manager holds all the EventListeners, and dispatches events among them.
{

    private static EventManager INSTANCE = new EventManager();  //Singleton pattern, as multiple managers is nonsensical.
    private HashMap<Class<? extends EventObject>, LinkedList<EventListener>> listeners;
    private int lockCount = 0;
    private LinkedList<ListenerStub> delayedListeners;

    public static EventManager getInstance()
    {
        return INSTANCE;
    }

    private EventManager()
    {
        listeners = new HashMap();
        delayedListeners = new LinkedList();
    }

    /*Adds a listener if there's nothing using the set of listeners, or delays it
     * until the usage is finished if something is.
     * Package protected because nothing other than EventListener's registerListener() 
     * should call it. Eqivalent statements apply for removeListener() below.
     */
    void addListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (lockCount == 0) {
            realAddListener(clazz, listener);
        } else {
            delayListenerOperation(clazz, listener, true);
        }
    }

    private void realAddListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (!listeners.containsKey(clazz)) {
            listeners.put(clazz, new LinkedList<EventListener>());
        }
        LinkedList<EventListener> list = listeners.get(clazz);
        list.add(listener);
        System.out.println("added");
    }

    void removeListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (lockCount == 0) {
            realRemoveListener(clazz, listener);
        } else {
            delayListenerOperation(clazz, listener, false);
        }
    }

    private void realRemoveListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        LinkedList<EventListener> list = listeners.get(clazz);
        list.remove(listener);
        System.out.println("removed");
    }

    private void delayListenerOperation(Class<? extends EventObject> clazz, EventListener listener, boolean add)
    {
        delayedListeners.add(new ListenerStub(clazz, listener, add));
        System.out.println("delayed");
    }

    public void dispatchEvent(EventObject event) 
    {
        ListenerLock l = new ListenerLock();
        dispatchEvent(event, event.getClass()); //Hiding recursive call from user
        l.release();
        if (event instanceof ResolvableEventObject) {
            ((ResolvableEventObject) event).resolve();
            //ResolvableEventObject is my class, it's just an EventObject with a
            //resolve() method.
        }
    }

    private void dispatchEvent(EventObject event, Class clazz)
    {
        LinkedList<EventListener> list = listeners.get(clazz);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                EventListener listener = list.get(i);
                listener.handleEvent(event);
            }
        }
        if (EventObject.class.isAssignableFrom(clazz.getSuperclass())) {
            dispatchEvent(event, clazz.getSuperclass());
        }
    }

    private void resolveDelayedListeners()
    {
        for (ListenerStub listenerStub : delayedListeners) {
            if (listenerStub.isAdd()) {
                realAddListener(listenerStub.getListenerClass(), listenerStub.getListener());
            } else {
                realRemoveListener(listenerStub.getListenerClass(), listenerStub.getListener());
            }
        }
    }

    private class ListenerStub /*
     * This class is used to hold the data of the listeners to be removed
     * until the lock on the listener set is released.
     */

    {

        private Class<? extends EventObject> clazz;
        private EventListener listener;
        private boolean add;

        ListenerStub(Class<? extends EventObject> clazz, EventListener listener, boolean add)
        {
            this.clazz = clazz;
            this.listener = listener;
            this.add = add;
        }

        public Class<? extends EventObject> getListenerClass()
        {
            return clazz;
        }

        public EventListener getListener()
        {
            return listener;
        }

        public boolean isAdd()
        {
            return add;
        }

        
    }

    public class ListenerLock
    /*
     * This is a basic lock that is used to prevent modifications to the list of
     * listeners. It releases when told, or when finalized if forgotten.
     */
    {

        boolean released = false;

        ListenerLock()
        {
            lockCount++;
        }

        public void release()
        {
            if (!released) {
                lockCount = Math.min(0, lockCount - 1);
                released = true;
                if (lockCount == 0) {
                    resolveDelayedListeners();
                }
            }
        }

        protected void finalize() throws Throwable
        {
            super.finalize();
            release();
        }
    }
}

EventListener.java

public final class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source = null;
    Method handler = null;
    Class clazz;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> clazz, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = clazz;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + clazz.getSimpleName() + ") found.");
        }
        this.clazz = clazz;
    }

    public EventListener(Class<? extends EventObject> clazz, Object source,
                         String handlingMethod)
    {
        this(clazz, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected, delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. Reference to listener
                                   //is required to deregister later.
    {
        EventManager.getInstance().addListener(clazz, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(clazz, this);
    }
}
public class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source;
    Method handler = null;
    Class E;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = E;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + E.getSimpleName() + ") found.");
        }
        this.E = E;
    }

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod)
    {
        this(E, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected,
                                                 //delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. 
                                   //Reference to listener is required to deregister later.
    {
        EventManager.getInstance().addListener(E, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(E, this);
    }
}

Note: this is the old code, kept for TwoThe's answer. See below for new code.

public class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source;
    Method handler = null;
    Class E;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = E;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + E.getSimpleName() + ") found.");
        }
        this.E = E;
    }

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod)
    {
        this(E, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected,
                                                 //delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. 
                                   //Reference to listener is required to deregister later.
    {
        EventManager.getInstance().addListener(E, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(E, this);
    }
}

New code:

EventManager.java

public final class EventManager //The manager holds all the EventListeners, and dispatches events among them.
{

    private static EventManager INSTANCE = new EventManager();  //Singleton pattern, as multiple managers is nonsensical.
    private HashMap<Class<? extends EventObject>, LinkedList<EventListener>> listeners;
    private int lockCount = 0;
    private LinkedList<ListenerStub> delayedListeners;

    public static EventManager getInstance()
    {
        return INSTANCE;
    }

    private EventManager()
    {
        listeners = new HashMap();
        delayedListeners = new LinkedList();
    }

    /*Adds a listener if there's nothing using the set of listeners, or delays it
     * until the usage is finished if something is.
     * Package protected because nothing other than EventListener's registerListener() 
     * should call it. Eqivalent statements apply for removeListener() below.
     */
    void addListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (lockCount == 0) {
            realAddListener(clazz, listener);
        } else {
            delayListenerOperation(clazz, listener, true);
        }
    }

    private void realAddListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (!listeners.containsKey(clazz)) {
            listeners.put(clazz, new LinkedList<EventListener>());
        }
        LinkedList<EventListener> list = listeners.get(clazz);
        list.add(listener);
        System.out.println("added");
    }

    void removeListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (lockCount == 0) {
            realRemoveListener(clazz, listener);
        } else {
            delayListenerOperation(clazz, listener, false);
        }
    }

    private void realRemoveListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        LinkedList<EventListener> list = listeners.get(clazz);
        list.remove(listener);
        System.out.println("removed");
    }

    private void delayListenerOperation(Class<? extends EventObject> clazz, EventListener listener, boolean add)
    {
        delayedListeners.add(new ListenerStub(clazz, listener, add));
        System.out.println("delayed");
    }

    public void dispatchEvent(EventObject event) 
    {
        ListenerLock l = new ListenerLock();
        dispatchEvent(event, event.getClass()); //Hiding recursive call from user
        l.release();
        if (event instanceof ResolvableEventObject) {
            ((ResolvableEventObject) event).resolve();
            //ResolvableEventObject is my class, it's just an EventObject with a
            //resolve() method.
        }
    }

    private void dispatchEvent(EventObject event, Class clazz)
    {
        LinkedList<EventListener> list = listeners.get(clazz);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                EventListener listener = list.get(i);
                listener.handleEvent(event);
            }
        }
        if (EventObject.class.isAssignableFrom(clazz.getSuperclass())) {
            dispatchEvent(event, clazz.getSuperclass());
        }
    }

    private void resolveDelayedListeners()
    {
        for (ListenerStub listenerStub : delayedListeners) {
            if (listenerStub.isAdd()) {
                realAddListener(listenerStub.getListenerClass(), listenerStub.getListener());
            } else {
                realRemoveListener(listenerStub.getListenerClass(), listenerStub.getListener());
            }
        }
    }

    private class ListenerStub /*
     * This class is used to hold the data of the listeners to be removed
     * until the lock on the listener set is released.
     */

    {

        private Class<? extends EventObject> clazz;
        private EventListener listener;
        private boolean add;

        ListenerStub(Class<? extends EventObject> clazz, EventListener listener, boolean add)
        {
            this.clazz = clazz;
            this.listener = listener;
            this.add = add;
        }

        public Class<? extends EventObject> getListenerClass()
        {
            return clazz;
        }

        public EventListener getListener()
        {
            return listener;
        }

        public boolean isAdd()
        {
            return add;
        }

        
    }

    public class ListenerLock
    /*
     * This is a basic lock that is used to prevent modifications to the list of
     * listeners. It releases when told, or when finalized if forgotten.
     */
    {

        boolean released = false;

        ListenerLock()
        {
            lockCount++;
        }

        public void release()
        {
            if (!released) {
                lockCount = Math.min(0, lockCount - 1);
                released = true;
                if (lockCount == 0) {
                    resolveDelayedListeners();
                }
            }
        }

        protected void finalize() throws Throwable
        {
            super.finalize();
            release();
        }
    }
}

EventListener.java

public final class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source = null;
    Method handler = null;
    Class clazz;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> clazz, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = clazz;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + clazz.getSimpleName() + ") found.");
        }
        this.clazz = clazz;
    }

    public EventListener(Class<? extends EventObject> clazz, Object source,
                         String handlingMethod)
    {
        this(clazz, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected, delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. Reference to listener
                                   //is required to deregister later.
    {
        EventManager.getInstance().addListener(clazz, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(clazz, this);
    }
}
Rollback to Revision 1
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

I have written a system for handling the dispatch of events between objects. (I need this functionality for a larger project I am working on. Objects should be able to dispatch events without knowing anything about the objects that will receive them, or vice versa. (bolded because this is important, andObjects should be able to dispatch events without knowing anything about the last person missed itobjects that will receive them.)

If this indicates that there is something seriously wrong with my larger design, please tell me.)

public final class EventManager //The manager holds all the EventListeners, 
                          //and dispatches events among them.
{

    private static EventManager INSTANCE = new EventManager();  //Singleton pattern, as multiple managers is nonsensical.
    private static EventManager INSTANCE = null;  
    private HashMap<Class<? extends EventObject>, LinkedList<EventListener>>ArrayList<EventListener>> listeners;
    private int lockCount = 0;
    private LinkedList<ListenerStub>ArrayList<ListenerStub> delayedListeners;

    public static EventManager getInstance()
    {
        if (INSTANCE == null) {
            INSTANCE = new EventManager();
        }
        return INSTANCE;
    }

    private EventManager()
    {
        listeners = new HashMap();
        delayedListeners = new LinkedListArrayList();
    }

    /*Adds a listener if there's nothing using the set of listeners, or delays it
     * until the usage is finished if something is.
     * Package protected because nothing other than EventListener's registerListener() 
     * should call it. Eqivalent statements apply for removeListener() below.
     */
    void addListener(Class<? extends EventObject> clazzc, EventListener listenerm)
    {
        if (lockCount == 0) {
            realAddListener(clazzc, listenerm);
        } else {
            delayListenerOperation(clazzc, listenerm, true);
        }
    }

    private void realAddListener(Class<? extends EventObject> clazzc, EventListener listenerm)
    {
        if (!listeners.containsKey(clazzc)) {
            listeners.put(clazzc, new LinkedList<EventListener>ArrayList<EventListener>());
        }
        LinkedList<EventListener>ArrayList<EventListener> list = listeners.get(clazzc);
        list.add(listenerm);
        System.out.println("added");
    }

    void removeListener(Class<? extends EventObject> clazzc, EventListener listenerm)
    {
        if (lockCount == 0) {
            realRemoveListener(clazzc, listenerm);
        } else {
            delayListenerOperation(clazzc, listenerm, false);
        }
    }

    private void realRemoveListener(Class<? extends EventObject> clazzc, EventListener listenerm)
    {
        LinkedList<EventListener>ArrayList<EventListener> list = listeners.get(clazzc);
        list.remove(listenerm);
        System.out.println("removed");
    }

    private void delayListenerOperation(Class<? extends EventObject> clazzc, EventListener listenerm, boolean add)
    {
        delayedListeners.add(new ListenerStub(clazzc, listenerm, add));
        System.out.println("delayed");
    }

    public void dispatchEvent(EventObject evente) 
    {
        ListenerLock l = new ListenerLock();
        //This odd construction seems to force garbage collection. 
        //Security measure to clean up old objects before dispatching events.
        System.gc();
        try {
            Thread.sleep(1);
        } catch (Exception ex) {
        }
        //end of odd construction
        dispatchEvent(evente, evente.getClass()); //Hiding recursive call from user
        l.release();
        if (evente instanceof ResolvableEventObject) {
            ((ResolvableEventObject) evente).resolve();
            //ResolvableEventObject is my class, it's just an EventObject with a
            //resolve() method.
        }
    }

    private void dispatchEvent(EventObject evente, Class clazzC)
    {
        LinkedList<EventListener>ArrayList<EventListener> list = listeners.get(clazzC);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                EventListener listener = list.get(i);
                listener.handleEvent(evente);
            }
        }
        if (EventObject.class.isAssignableFrom(clazzC.getSuperclass())) {
            dispatchEvent(evente, clazzC.getSuperclass());
        }
    }

    private void resolveDelayedListeners()
    {
        for (ListenerStub listenerStub : delayedListeners) {
            if (listenerStub.isAdd()) {
                realAddListener(listenerStub.getListenerClassgetC(), listenerStub.getListenergetM());
            } else {
                realRemoveListener(listenerStub.getListenerClassgetC(), listenerStub.getListenergetM());
            }
        }
    }

    private class ListenerStub /*
     * This class is used to hold the data of the listeners to be removed
     * until the lock on the listener set is released.
     */

    {

        private Class<? extends EventObject> clazz;c;
        private EventListener listener;m;
        private boolean add;

        ListenerStub(Class<? extends EventObject> clazzc, EventListener listenerm, boolean add)
        {
            this.clazzc = clazz;c;
            this.listenerm = listener;m;
            this.add = add;
        }

        public Class<? extends EventObject> getListenerClassgetC()
        {
            return clazz;c;
        }

        public EventListener getListenergetM()
        {
            return listener;m;
        }

        public boolean isAdd()
        {
            return add;
        }

        
    }

    public class ListenerLock
    /*
     * This is a basic lock that is used to prevent modifications to the list of
     * listeners. It releases when told, or when finalized if forgotten.
     */
    {

        boolean released = false;

        ListenerLock()
        {
            lockCount++;
        }

        public void release()
        {
            if (!released) {
                lockCount = Math.min(0, lockCount - 1);
                released = true;
                if (lockCount == 0) {
                    resolveDelayedListeners();
                }
            }
        }

        protected void finalize() throws Throwable
        {
            super.finalize();
            release();
        }
    }
}
public final class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source = null;source;
    Method handler = null;
    Class clazz;E;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> clazzE, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = clazz;E;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + clazzE.getSimpleName() + ") found.");
        }
        this.clazzE = clazz;E;
    }

    public EventListener(Class<? extends EventObject> clazzE, Object source,
                         String handlingMethod)
    {
        this(clazzE, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected,
                                                 //delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. Reference to listener
                                   //Reference to listener is required to deregister later.
    {
        EventManager.getInstance().addListener(clazzE, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(clazzE, this);
    }
}

Note: I have edited this to take in the useful parts of TwoThe's answer, but not all of his answer could be used, as it doesn't fit my use case.

I have written a system for handling the dispatch of events between objects. (I need this functionality for a larger project I am working on. Objects should be able to dispatch events without knowing anything about the objects that will receive them, or vice versa. (bolded because this is important, and the last person missed it.)

If this indicates that there is something seriously wrong with my larger design, please tell me.

public final class EventManager //The manager holds all the EventListeners, and dispatches events among them.
{

    private static EventManager INSTANCE = new EventManager();  //Singleton pattern, as multiple managers is nonsensical.
    private HashMap<Class<? extends EventObject>, LinkedList<EventListener>> listeners;
    private int lockCount = 0;
    private LinkedList<ListenerStub> delayedListeners;

    public static EventManager getInstance()
    {
        return INSTANCE;
    }

    private EventManager()
    {
        listeners = new HashMap();
        delayedListeners = new LinkedList();
    }

    /*Adds a listener if there's nothing using the set of listeners, or delays it
     * until the usage is finished if something is.
     * Package protected because nothing other than EventListener's registerListener() 
     * should call it. Eqivalent statements apply for removeListener() below.
     */
    void addListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (lockCount == 0) {
            realAddListener(clazz, listener);
        } else {
            delayListenerOperation(clazz, listener, true);
        }
    }

    private void realAddListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (!listeners.containsKey(clazz)) {
            listeners.put(clazz, new LinkedList<EventListener>());
        }
        LinkedList<EventListener> list = listeners.get(clazz);
        list.add(listener);
        System.out.println("added");
    }

    void removeListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (lockCount == 0) {
            realRemoveListener(clazz, listener);
        } else {
            delayListenerOperation(clazz, listener, false);
        }
    }

    private void realRemoveListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        LinkedList<EventListener> list = listeners.get(clazz);
        list.remove(listener);
        System.out.println("removed");
    }

    private void delayListenerOperation(Class<? extends EventObject> clazz, EventListener listener, boolean add)
    {
        delayedListeners.add(new ListenerStub(clazz, listener, add));
        System.out.println("delayed");
    }

    public void dispatchEvent(EventObject event) 
    {
        ListenerLock l = new ListenerLock();
        //This odd construction seems to force garbage collection. 
        //Security measure to clean up old objects before dispatching events.
        System.gc();
        try {
            Thread.sleep(1);
        } catch (Exception ex) {
        }
        //end of odd construction
        dispatchEvent(event, event.getClass()); //Hiding recursive call from user
        l.release();
        if (event instanceof ResolvableEventObject) {
            ((ResolvableEventObject) event).resolve();
            //ResolvableEventObject is my class, it's just an EventObject with a
            //resolve() method.
        }
    }

    private void dispatchEvent(EventObject event, Class clazz)
    {
        LinkedList<EventListener> list = listeners.get(clazz);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                EventListener listener = list.get(i);
                listener.handleEvent(event);
            }
        }
        if (EventObject.class.isAssignableFrom(clazz.getSuperclass())) {
            dispatchEvent(event, clazz.getSuperclass());
        }
    }

    private void resolveDelayedListeners()
    {
        for (ListenerStub listenerStub : delayedListeners) {
            if (listenerStub.isAdd()) {
                realAddListener(listenerStub.getListenerClass(), listenerStub.getListener());
            } else {
                realRemoveListener(listenerStub.getListenerClass(), listenerStub.getListener());
            }
        }
    }

    private class ListenerStub /*
     * This class is used to hold the data of the listeners to be removed
     * until the lock on the listener set is released.
     */

    {

        private Class<? extends EventObject> clazz;
        private EventListener listener;
        private boolean add;

        ListenerStub(Class<? extends EventObject> clazz, EventListener listener, boolean add)
        {
            this.clazz = clazz;
            this.listener = listener;
            this.add = add;
        }

        public Class<? extends EventObject> getListenerClass()
        {
            return clazz;
        }

        public EventListener getListener()
        {
            return listener;
        }

        public boolean isAdd()
        {
            return add;
        }

        
    }

    public class ListenerLock
    /*
     * This is a basic lock that is used to prevent modifications to the list of
     * listeners. It releases when told, or when finalized if forgotten.
     */
    {

        boolean released = false;

        ListenerLock()
        {
            lockCount++;
        }

        public void release()
        {
            if (!released) {
                lockCount = Math.min(0, lockCount - 1);
                released = true;
                if (lockCount == 0) {
                    resolveDelayedListeners();
                }
            }
        }

        protected void finalize() throws Throwable
        {
            super.finalize();
            release();
        }
    }
}
public final class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source = null;
    Method handler = null;
    Class clazz;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> clazz, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = clazz;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + clazz.getSimpleName() + ") found.");
        }
        this.clazz = clazz;
    }

    public EventListener(Class<? extends EventObject> clazz, Object source,
                         String handlingMethod)
    {
        this(clazz, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected, delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. Reference to listener
                                   //is required to deregister later.
    {
        EventManager.getInstance().addListener(clazz, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(clazz, this);
    }
}

Note: I have edited this to take in the useful parts of TwoThe's answer, but not all of his answer could be used, as it doesn't fit my use case.

I have written a system for handling the dispatch of events between objects. (I need this functionality for a larger project I am working on. Objects should be able to dispatch events without knowing anything about the objects that will receive them. If this indicates that there is something seriously wrong with my larger design, please tell me.)

public class EventManager //The manager holds all the EventListeners, 
                          //and dispatches events among them.
{
    //Singleton pattern, as multiple managers is nonsensical.
    private static EventManager INSTANCE = null;  
    private HashMap<Class<? extends EventObject>, ArrayList<EventListener>> listeners;
    private int lockCount = 0;
    private ArrayList<ListenerStub> delayedListeners;

    public static EventManager getInstance()
    {
        if (INSTANCE == null) {
            INSTANCE = new EventManager();
        }
        return INSTANCE;
    }

    private EventManager()
    {
        listeners = new HashMap();
        delayedListeners = new ArrayList();
    }

    /*Adds a listener if there's nothing using the set of listeners, or delays it
     * until the usage is finished if something is.
     * Package protected because nothing other than EventListener's registerListener() 
     * should call it. Eqivalent statements apply for removeListener() below.
     */
    void addListener(Class<? extends EventObject> c, EventListener m)
    {
        if (lockCount == 0) {
            realAddListener(c, m);
        } else {
            delayListenerOperation(c, m, true);
        }
    }

    void realAddListener(Class<? extends EventObject> c, EventListener m)
    {
        if (!listeners.containsKey(c)) {
            listeners.put(c, new ArrayList<EventListener>());
        }
        ArrayList<EventListener> list = listeners.get(c);
        list.add(m);
        System.out.println("added");
    }

    void removeListener(Class<? extends EventObject> c, EventListener m)
    {
        if (lockCount == 0) {
            realRemoveListener(c, m);
        } else {
            delayListenerOperation(c, m, false);
        }
    }

    private void realRemoveListener(Class<? extends EventObject> c, EventListener m)
    {
        ArrayList<EventListener> list = listeners.get(c);
        list.remove(m);
        System.out.println("removed");
    }

    private void delayListenerOperation(Class<? extends EventObject> c, EventListener m, boolean add)
    {
        delayedListeners.add(new ListenerStub(c, m, add));
        System.out.println("delayed");
    }

    public void dispatchEvent(EventObject e) 
    {
        ListenerLock l = new ListenerLock();
        //This odd construction seems to force garbage collection. 
        //Security measure to clean up old objects before dispatching events.
        System.gc();
        try {
            Thread.sleep(1);
        } catch (Exception ex) {
        }
        //end of odd construction
        dispatchEvent(e, e.getClass()); //Hiding recursive call from user
        l.release();
        if (e instanceof ResolvableEventObject) {
            ((ResolvableEventObject) e).resolve();
            //ResolvableEventObject is my class, it's just an EventObject with a
            //resolve() method.
        }
    }

    private void dispatchEvent(EventObject e, Class C)
    {
        ArrayList<EventListener> list = listeners.get(C);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                EventListener listener = list.get(i);
                listener.handleEvent(e);
            }
        }
        if (EventObject.class.isAssignableFrom(C.getSuperclass())) {
            dispatchEvent(e, C.getSuperclass());
        }
    }

    private void resolveDelayedListeners()
    {
        for (ListenerStub listenerStub : delayedListeners) {
            if (listenerStub.isAdd()) {
                realAddListener(listenerStub.getC(), listenerStub.getM());
            } else {
                realRemoveListener(listenerStub.getC(), listenerStub.getM());
            }
        }
    }

    private class ListenerStub /*
     * This class is used to hold the data of the listeners to be removed
     * until the lock on the listener set is released.
     */

    {

        private Class<? extends EventObject> c;
        private EventListener m;
        private boolean add;

        ListenerStub(Class<? extends EventObject> c, EventListener m, boolean add)
        {
            this.c = c;
            this.m = m;
            this.add = add;
        }

        public Class<? extends EventObject> getC()
        {
            return c;
        }

        public EventListener getM()
        {
            return m;
        }

        public boolean isAdd()
        {
            return add;
        }
    }

    public class ListenerLock
    /*
     * This is a basic lock that is used to prevent modifications to the list of
     * listeners. It releases when told, or when finalized if forgotten.
     */
    {

        boolean released = false;

        ListenerLock()
        {
            lockCount++;
        }

        public void release()
        {
            if (!released) {
                lockCount = Math.min(0, lockCount - 1);
                released = true;
                if (lockCount == 0) {
                    resolveDelayedListeners();
                }
            }
        }

        protected void finalize() throws Throwable
        {
            super.finalize();
            release();
        }
    }
}
public class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source;
    Method handler = null;
    Class E;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = E;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + E.getSimpleName() + ") found.");
        }
        this.E = E;
    }

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod)
    {
        this(E, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected,
                                                 //delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. 
                                   //Reference to listener is required to deregister later.
    {
        EventManager.getInstance().addListener(E, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(E, this);
    }
}
Accepted useful parts of TwoThe's answer.
Source Link

I have written a system for handling the dispatch of events between objects. (I need this functionality for a larger project I am working on. Objects should be able to dispatch events without knowing anything aboutObjects should be able to dispatch events without knowing anything about the objects that will receive them, or vice versa. (bolded because this is important, and the objects that will receive themlast person missed it.)

If this indicates that there is something seriously wrong with my larger design, please tell me.)

public final class EventManager //The manager holds all the EventListeners, 
                          //and dispatches events among them.
{

    private static EventManager INSTANCE = new EventManager();  //Singleton pattern, as multiple managers is nonsensical.
    private static EventManager INSTANCE = null;  
    private HashMap<Class<? extends EventObject>, ArrayList<EventListener>>LinkedList<EventListener>> listeners;
    private int lockCount = 0;
    private ArrayList<ListenerStub>LinkedList<ListenerStub> delayedListeners;

    public static EventManager getInstance()
    {
        if (INSTANCE == null) {
            INSTANCE = new EventManager();
        }
        return INSTANCE;
    }

    private EventManager()
    {
        listeners = new HashMap();
        delayedListeners = new ArrayListLinkedList();
    }

    /*Adds a listener if there's nothing using the set of listeners, or delays it
     * until the usage is finished if something is.
     * Package protected because nothing other than EventListener's registerListener() 
     * should call it. Eqivalent statements apply for removeListener() below.
     */
    void addListener(Class<? extends EventObject> cclazz, EventListener mlistener)
    {
        if (lockCount == 0) {
            realAddListener(cclazz, mlistener);
        } else {
            delayListenerOperation(cclazz, mlistener, true);
        }
    }

    private void realAddListener(Class<? extends EventObject> cclazz, EventListener mlistener)
    {
        if (!listeners.containsKey(cclazz)) {
            listeners.put(cclazz, new ArrayList<EventListener>LinkedList<EventListener>());
        }
        ArrayList<EventListener>LinkedList<EventListener> list = listeners.get(cclazz);
        list.add(mlistener);
        System.out.println("added");
    }

    void removeListener(Class<? extends EventObject> cclazz, EventListener mlistener)
    {
        if (lockCount == 0) {
            realRemoveListener(cclazz, mlistener);
        } else {
            delayListenerOperation(cclazz, mlistener, false);
        }
    }

    private void realRemoveListener(Class<? extends EventObject> cclazz, EventListener mlistener)
    {
        ArrayList<EventListener>LinkedList<EventListener> list = listeners.get(cclazz);
        list.remove(mlistener);
        System.out.println("removed");
    }

    private void delayListenerOperation(Class<? extends EventObject> cclazz, EventListener mlistener, boolean add)
    {
        delayedListeners.add(new ListenerStub(cclazz, mlistener, add));
        System.out.println("delayed");
    }

    public void dispatchEvent(EventObject eevent) 
    {
        ListenerLock l = new ListenerLock();
        //This odd construction seems to force garbage collection. 
        //Security measure to clean up old objects before dispatching events.
        System.gc();
        try {
            Thread.sleep(1);
        } catch (Exception ex) {
        }
        //end of odd construction
        dispatchEvent(eevent, eevent.getClass()); //Hiding recursive call from user
        l.release();
        if (eevent instanceof ResolvableEventObject) {
            ((ResolvableEventObject) eevent).resolve();
            //ResolvableEventObject is my class, it's just an EventObject with a
            //resolve() method.
        }
    }

    private void dispatchEvent(EventObject eevent, Class Cclazz)
    {
        ArrayList<EventListener>LinkedList<EventListener> list = listeners.get(Cclazz);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                EventListener listener = list.get(i);
                listener.handleEvent(eevent);
            }
        }
        if (EventObject.class.isAssignableFrom(Cclazz.getSuperclass())) {
            dispatchEvent(eevent, Cclazz.getSuperclass());
        }
    }

    private void resolveDelayedListeners()
    {
        for (ListenerStub listenerStub : delayedListeners) {
            if (listenerStub.isAdd()) {
                realAddListener(listenerStub.getCgetListenerClass(), listenerStub.getMgetListener());
            } else {
                realRemoveListener(listenerStub.getCgetListenerClass(), listenerStub.getMgetListener());
            }
        }
    }

    private class ListenerStub /*
     * This class is used to hold the data of the listeners to be removed
     * until the lock on the listener set is released.
     */

    {

        private Class<? extends EventObject> c;clazz;
        private EventListener m;listener;
        private boolean add;

        ListenerStub(Class<? extends EventObject> cclazz, EventListener mlistener, boolean add)
        {
            this.cclazz = c;clazz;
            this.mlistener = m;listener;
            this.add = add;
        }

        public Class<? extends EventObject> getCgetListenerClass()
        {
            return c;clazz;
        }

        public EventListener getMgetListener()
        {
            return m;listener;
        }

        public boolean isAdd()
        {
            return add;
        }

        
    }

    public class ListenerLock
    /*
     * This is a basic lock that is used to prevent modifications to the list of
     * listeners. It releases when told, or when finalized if forgotten.
     */
    {

        boolean released = false;

        ListenerLock()
        {
            lockCount++;
        }

        public void release()
        {
            if (!released) {
                lockCount = Math.min(0, lockCount - 1);
                released = true;
                if (lockCount == 0) {
                    resolveDelayedListeners();
                }
            }
        }

        protected void finalize() throws Throwable
        {
            super.finalize();
            release();
        }
    }
}
public final class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source;source = null;
    Method handler = null;
    Class E;clazz;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> Eclazz, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = E;clazz;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + Eclazz.getSimpleName() + ") found.");
        }
        this.Eclazz = E;clazz;
    }

    public EventListener(Class<? extends EventObject> Eclazz, Object source,
                         String handlingMethod)
    {
        this(Eclazz, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected,
                                                 //delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. Reference to listener
                                   //Reference to listener is required to deregister later.
    {
        EventManager.getInstance().addListener(Eclazz, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(Eclazz, this);
    }
}

Note: I have edited this to take in the useful parts of TwoThe's answer, but not all of his answer could be used, as it doesn't fit my use case.

I have written a system for handling the dispatch of events between objects. (I need this functionality for a larger project I am working on. Objects should be able to dispatch events without knowing anything about the objects that will receive them. If this indicates that there is something seriously wrong with my larger design, please tell me.)

public class EventManager //The manager holds all the EventListeners, 
                          //and dispatches events among them.
{
    //Singleton pattern, as multiple managers is nonsensical.
    private static EventManager INSTANCE = null;  
    private HashMap<Class<? extends EventObject>, ArrayList<EventListener>> listeners;
    private int lockCount = 0;
    private ArrayList<ListenerStub> delayedListeners;

    public static EventManager getInstance()
    {
        if (INSTANCE == null) {
            INSTANCE = new EventManager();
        }
        return INSTANCE;
    }

    private EventManager()
    {
        listeners = new HashMap();
        delayedListeners = new ArrayList();
    }

    /*Adds a listener if there's nothing using the set of listeners, or delays it
     * until the usage is finished if something is.
     * Package protected because nothing other than EventListener's registerListener() 
     * should call it. Eqivalent statements apply for removeListener() below.
     */
    void addListener(Class<? extends EventObject> c, EventListener m)
    {
        if (lockCount == 0) {
            realAddListener(c, m);
        } else {
            delayListenerOperation(c, m, true);
        }
    }

    void realAddListener(Class<? extends EventObject> c, EventListener m)
    {
        if (!listeners.containsKey(c)) {
            listeners.put(c, new ArrayList<EventListener>());
        }
        ArrayList<EventListener> list = listeners.get(c);
        list.add(m);
        System.out.println("added");
    }

    void removeListener(Class<? extends EventObject> c, EventListener m)
    {
        if (lockCount == 0) {
            realRemoveListener(c, m);
        } else {
            delayListenerOperation(c, m, false);
        }
    }

    private void realRemoveListener(Class<? extends EventObject> c, EventListener m)
    {
        ArrayList<EventListener> list = listeners.get(c);
        list.remove(m);
        System.out.println("removed");
    }

    private void delayListenerOperation(Class<? extends EventObject> c, EventListener m, boolean add)
    {
        delayedListeners.add(new ListenerStub(c, m, add));
        System.out.println("delayed");
    }

    public void dispatchEvent(EventObject e) 
    {
        ListenerLock l = new ListenerLock();
        //This odd construction seems to force garbage collection. 
        //Security measure to clean up old objects before dispatching events.
        System.gc();
        try {
            Thread.sleep(1);
        } catch (Exception ex) {
        }
        //end of odd construction
        dispatchEvent(e, e.getClass()); //Hiding recursive call from user
        l.release();
        if (e instanceof ResolvableEventObject) {
            ((ResolvableEventObject) e).resolve();
            //ResolvableEventObject is my class, it's just an EventObject with a
            //resolve() method.
        }
    }

    private void dispatchEvent(EventObject e, Class C)
    {
        ArrayList<EventListener> list = listeners.get(C);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                EventListener listener = list.get(i);
                listener.handleEvent(e);
            }
        }
        if (EventObject.class.isAssignableFrom(C.getSuperclass())) {
            dispatchEvent(e, C.getSuperclass());
        }
    }

    private void resolveDelayedListeners()
    {
        for (ListenerStub listenerStub : delayedListeners) {
            if (listenerStub.isAdd()) {
                realAddListener(listenerStub.getC(), listenerStub.getM());
            } else {
                realRemoveListener(listenerStub.getC(), listenerStub.getM());
            }
        }
    }

    private class ListenerStub /*
     * This class is used to hold the data of the listeners to be removed
     * until the lock on the listener set is released.
     */

    {

        private Class<? extends EventObject> c;
        private EventListener m;
        private boolean add;

        ListenerStub(Class<? extends EventObject> c, EventListener m, boolean add)
        {
            this.c = c;
            this.m = m;
            this.add = add;
        }

        public Class<? extends EventObject> getC()
        {
            return c;
        }

        public EventListener getM()
        {
            return m;
        }

        public boolean isAdd()
        {
            return add;
        }
    }

    public class ListenerLock
    /*
     * This is a basic lock that is used to prevent modifications to the list of
     * listeners. It releases when told, or when finalized if forgotten.
     */
    {

        boolean released = false;

        ListenerLock()
        {
            lockCount++;
        }

        public void release()
        {
            if (!released) {
                lockCount = Math.min(0, lockCount - 1);
                released = true;
                if (lockCount == 0) {
                    resolveDelayedListeners();
                }
            }
        }

        protected void finalize() throws Throwable
        {
            super.finalize();
            release();
        }
    }
}
public class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source;
    Method handler = null;
    Class E;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = E;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + E.getSimpleName() + ") found.");
        }
        this.E = E;
    }

    public EventListener(Class<? extends EventObject> E, Object source,
                         String handlingMethod)
    {
        this(E, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected,
                                                 //delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. 
                                   //Reference to listener is required to deregister later.
    {
        EventManager.getInstance().addListener(E, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(E, this);
    }
}

I have written a system for handling the dispatch of events between objects. (I need this functionality for a larger project I am working on. Objects should be able to dispatch events without knowing anything about the objects that will receive them, or vice versa. (bolded because this is important, and the last person missed it.)

If this indicates that there is something seriously wrong with my larger design, please tell me.

public final class EventManager //The manager holds all the EventListeners, and dispatches events among them.
{

    private static EventManager INSTANCE = new EventManager();  //Singleton pattern, as multiple managers is nonsensical.
    private HashMap<Class<? extends EventObject>, LinkedList<EventListener>> listeners;
    private int lockCount = 0;
    private LinkedList<ListenerStub> delayedListeners;

    public static EventManager getInstance()
    {
        return INSTANCE;
    }

    private EventManager()
    {
        listeners = new HashMap();
        delayedListeners = new LinkedList();
    }

    /*Adds a listener if there's nothing using the set of listeners, or delays it
     * until the usage is finished if something is.
     * Package protected because nothing other than EventListener's registerListener() 
     * should call it. Eqivalent statements apply for removeListener() below.
     */
    void addListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (lockCount == 0) {
            realAddListener(clazz, listener);
        } else {
            delayListenerOperation(clazz, listener, true);
        }
    }

    private void realAddListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (!listeners.containsKey(clazz)) {
            listeners.put(clazz, new LinkedList<EventListener>());
        }
        LinkedList<EventListener> list = listeners.get(clazz);
        list.add(listener);
        System.out.println("added");
    }

    void removeListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        if (lockCount == 0) {
            realRemoveListener(clazz, listener);
        } else {
            delayListenerOperation(clazz, listener, false);
        }
    }

    private void realRemoveListener(Class<? extends EventObject> clazz, EventListener listener)
    {
        LinkedList<EventListener> list = listeners.get(clazz);
        list.remove(listener);
        System.out.println("removed");
    }

    private void delayListenerOperation(Class<? extends EventObject> clazz, EventListener listener, boolean add)
    {
        delayedListeners.add(new ListenerStub(clazz, listener, add));
        System.out.println("delayed");
    }

    public void dispatchEvent(EventObject event) 
    {
        ListenerLock l = new ListenerLock();
        //This odd construction seems to force garbage collection. 
        //Security measure to clean up old objects before dispatching events.
        System.gc();
        try {
            Thread.sleep(1);
        } catch (Exception ex) {
        }
        //end of odd construction
        dispatchEvent(event, event.getClass()); //Hiding recursive call from user
        l.release();
        if (event instanceof ResolvableEventObject) {
            ((ResolvableEventObject) event).resolve();
            //ResolvableEventObject is my class, it's just an EventObject with a
            //resolve() method.
        }
    }

    private void dispatchEvent(EventObject event, Class clazz)
    {
        LinkedList<EventListener> list = listeners.get(clazz);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                EventListener listener = list.get(i);
                listener.handleEvent(event);
            }
        }
        if (EventObject.class.isAssignableFrom(clazz.getSuperclass())) {
            dispatchEvent(event, clazz.getSuperclass());
        }
    }

    private void resolveDelayedListeners()
    {
        for (ListenerStub listenerStub : delayedListeners) {
            if (listenerStub.isAdd()) {
                realAddListener(listenerStub.getListenerClass(), listenerStub.getListener());
            } else {
                realRemoveListener(listenerStub.getListenerClass(), listenerStub.getListener());
            }
        }
    }

    private class ListenerStub /*
     * This class is used to hold the data of the listeners to be removed
     * until the lock on the listener set is released.
     */

    {

        private Class<? extends EventObject> clazz;
        private EventListener listener;
        private boolean add;

        ListenerStub(Class<? extends EventObject> clazz, EventListener listener, boolean add)
        {
            this.clazz = clazz;
            this.listener = listener;
            this.add = add;
        }

        public Class<? extends EventObject> getListenerClass()
        {
            return clazz;
        }

        public EventListener getListener()
        {
            return listener;
        }

        public boolean isAdd()
        {
            return add;
        }

        
    }

    public class ListenerLock
    /*
     * This is a basic lock that is used to prevent modifications to the list of
     * listeners. It releases when told, or when finalized if forgotten.
     */
    {

        boolean released = false;

        ListenerLock()
        {
            lockCount++;
        }

        public void release()
        {
            if (!released) {
                lockCount = Math.min(0, lockCount - 1);
                released = true;
                if (lockCount == 0) {
                    resolveDelayedListeners();
                }
            }
        }

        protected void finalize() throws Throwable
        {
            super.finalize();
            release();
        }
    }
}
public final class EventListener
{
/*
 * An EventListener pairs a class (EventObject or a subtype) with a method on an object.
 * It can hold either a strong or a weak reference to the object, and defaults weak.
 * It uses reflection to call the specified method.
 */
    Object source = null;
    Method handler = null;
    Class clazz;
    WeakReference<Object> sourceReference;

    public EventListener(Class<? extends EventObject> clazz, Object source,
                         String handlingMethod, boolean weakReference)
    {

        if (weakReference) {
            this.source = null;
            sourceReference = new WeakReference(source);
        } else {
            this.source = source;
            sourceReference = null;
        }
        /*
         * This structure sets up the call to the specified method on the specified
         * object. If no method is found for the specified class to listen to,
         * superclasses are tried until either a valid method is found or EventObject
         * is reached with no match.
         */
        Class C = clazz;
        while (EventObject.class.isAssignableFrom(C) && handler == null) {
            try {
                this.handler = source.getClass().getMethod(handlingMethod, C);
            } catch (NoSuchMethodException e) {
                C = C.getSuperclass();
            }
        }
        if (handler == null) {
            throw new IllegalArgumentException("No method with the signature: "
                                               + handlingMethod + "("
                                               + clazz.getSimpleName() + ") found.");
        }
        this.clazz = clazz;
    }

    public EventListener(Class<? extends EventObject> clazz, Object source,
                         String handlingMethod)
    {
        this(clazz, source, handlingMethod, true);
    }

    void handleEvent(EventObject event) //package protected because it should only be
                                        //called by EventManager's dispatchEvent().
    {
        if (source == null) { //source == null iff using weak references.
            if (sourceReference.get() == null) { //referenced object garbage collected, delete listener.
                deregisterListener();
                return;
            }
            try {
                handler.invoke(sourceReference.get(), event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        } else {
            try {
                handler.invoke(source, event);
            } catch (IllegalAccessException | IllegalArgumentException |
                     InvocationTargetException e) {
                throw new IllegalArgumentException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerListener() //registers a listener with the manager. Reference to listener
                                   //is required to deregister later.
    {
        EventManager.getInstance().addListener(clazz, this);
    }

    public void deregisterListener()
    {
        EventManager.getInstance().removeListener(clazz, this);
    }
}

Note: I have edited this to take in the useful parts of TwoThe's answer, but not all of his answer could be used, as it doesn't fit my use case.

Source Link
Loading