1

Event dispatcher interface

public interface EventDispatcher {
    <T> EventListener<T> addEventListener(EventListener<T> l);
    <T> void removeEventListener(EventListener<T> l);
}

Implementation

public class DefaultEventDispatcher implements EventDispatcher {

@SuppressWarnings("unchecked")
private Map<Class, Set<EventListener>> listeners = new HashMap<Class, Set<EventListener>>();

public void addSupportedEvent(Class eventType) {
    listeners.put(eventType, new HashSet<EventListener>());
}

@Override
public <T> EventListener<T> addEventListener(EventListener<T> l) {
    Set<EventListener> lsts = listeners.get(T); // ****** error: cannot resolve T
    if (lsts == null) throw new RuntimeException("Unsupported event type");
    if (!lsts.add(l)) throw new RuntimeException("Listener already added");
    return l;
}

@Override
public <T> void removeEventListener(EventListener<T> l) {
    Set<EventListener> lsts = listeners.get(T); // ************* same error
    if (lsts == null) throw new RuntimeException("Unsupported event type");
    if (!lsts.remove(l)) throw new RuntimeException("Listener is not here");
}

}

Usage

    EventListener<ShapeAddEvent> l = addEventListener(new EventListener<ShapeAddEvent>() {
        @Override
        public void onEvent(ShapeAddEvent event) {
            // TODO Auto-generated method stub

        }
    });
    removeEventListener(l);

I've marked two errors with a comment above (in the implementation). Is there any way to get runtime access to this information?

Bart van Heukelom
  • 43,244
  • 59
  • 186
  • 301

2 Answers2

2

No, you can't refer 'T' at runtime.

http://java.sun.com/docs/books/tutorial/java/generics/erasure.html

update
But something like this would achieve similar effect

abstract class EventListener<T> {
    private Class<T> type;
    EventListener(Class<T> type) {
        this.type = type;
    }
    Class<T> getType() {
        return type;
    }

    abstract void onEvent(T t);
}

And to create listener

EventListener<String> e = new EventListener<String>(String.class) {
    public void onEvent(String event) {
    }
};
e.getType();
Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
  • 1
    Too bad, there goes my neat event model – Bart van Heukelom Jun 07 '10 at 22:49
  • There're still options, it doesn't have to be ugly :) – Nikita Rybak Jun 07 '10 at 22:55
  • I've run into similar obstacles and you might at least be able to turn new EventListener(String.class) into new EventListener(String.class) using type inference so that you only have to declare the type once on each side. – Matt H Jun 07 '10 at 23:30
  • @Matt Doesn't seem to work for me. It usually works for "EventListener(T param)" type of declaration, I guess "EventListener(Class param)" is beyond type inference capabilities. – Nikita Rybak Jun 07 '10 at 23:37
  • Hmm, I tried it myself and it seems you are right. Sorry. I have sort of gotten around the multiple type declarations myself by using a static factory method, but since you are implementing an anonymous class, I don't think that will work. – Matt H Jun 08 '10 at 03:59
0

You can't do it in the approach you are trying, due to erasure. However, with a little change in the design I believe you can achieve what you need. Consider adding the following method to EventListener interface:

public Class<T> getEventClass();

Every EventListener implementation has to state the class of events it works with (I assume that T stands for an event type). Now you can invoke this method in your addEventListener method, and determine the type at runtime.

Eyal Schneider
  • 22,166
  • 5
  • 47
  • 78