views:

41

answers:

2

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?

+1  A: 

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
Too bad, there goes my neat event model
Bart van Heukelom
There're still options, it doesn't have to be ugly :)
Nikita Rybak
I've run into similar obstacles and you might at least be able to turn new EventListener<String>(String.class) into new EventListener(String.class) using type inference so that you only have to declare the type once on each side.
Matt
@Matt Doesn't seem to work for me. It usually works for "EventListener(T param)" type of declaration, I guess "EventListener(Class<T> param)" is beyond type inference capabilities.
Nikita Rybak
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
A: 

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