2

I'm working on a program that need to react to different custom events and listeners.

I'm trying to make it short using generics, the fact is, I don't understand where is my mistake.

Here my code :

The listeners and the event :

public interface MyFirstListener extends EventListener {
    void firstRequest(String text);
}

public interface MySecondListener extends EventListener {
    void secondRequest(String text);
}

public class MyEvent extends EventObject {
    private String text = null;

    public MyEvent(Object source, String text) {
        super(source);
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

And here the tricky code :

public class MyProblem {

    private EventListenerList listenerList = new EventListenerList();

    public MyProblem() {}

    public <T extends EventListener> void addListener(T listener) {
        listenerList.add(T.class, listener); // Here I have : Illegal class literal for the type parameter T
    }

    public <T extends EventListener> void removeListener(T listener) {
        listenerList.remove(T.class, listener); // Here I have : Illegal class literal for the type parameter T
    }

    void fireFirstRequestEvent(MyEvent evt) {
        for (MyFirstListener listener : listenerList.getListeners(MyFirstListener.class)) {
            listener.firstRequest(evt.getText());
        }
    }   

    void fireSecondRequestEvent(MyEvent evt) {
        for (MySecondListener listener : listenerList.getListeners(MySecondListener.class)) {
            listener.secondRequest(evt.getText());
        }
    }   

    public static void main(String[] args) {

        FirstClass first = new FirstClass();
        SecondClass second = new SecondClass();

        MyProblem problem = new MyProblem();
        problem.addListener(first);
        problem.addListener(second);
    }
}


class FirstClass implements MyFirstListener {
    @Override
    public void firstRequest(String text) {
        // Do Something
    }
}

class SecondClass implements MySecondListener {
    @Override
    public void secondRequest(String text) {
        // Do Something
    }
}

The problem is in the methods addListeners and removeListeners, I have this error : Illegal class literal for the type parameter T I don't get the trick to catch it.

I try also this code :

listenerList.add(listener.getClass(), listener); // Here I have : The method add(Class<T>, T) in the type EventListenerList is not applicable for the arguments (Class<capture#1-of ? extends EventListener>, T)

I try some other thing without result and I don't find any solution that match my code.

Anybody have any solution or it is unsolvable ? My goal is to have the shorter code possible and even pretty.

Thank's

MonkeyJLuffy
  • 195
  • 3
  • 15
  • 2
    [Type erasure](http://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens) prevents you from using `T.class` at runtime. – Andy Thomas Feb 22 '17 at 17:06
  • How does your `EventListenerList` look like (especially the add and remove methods)? – Calculator Feb 22 '17 at 17:24
  • 2
    @Calculator It probably looks like [javax.swing.event.EventListenerList](http://docs.oracle.com/javase/8/docs/api/javax/swing/event/EventListenerList.html). – VGR Feb 22 '17 at 17:47
  • 1
    Did you try to cast the the result of listener.getClass? Like listenerList.add((Class)listener.getClass(), listener). You will get a warning, but the code should be correct. – Dominik Feb 22 '17 at 20:03
  • @Calculator Yeah it's the javax.swing.event.EventListenerList; – MonkeyJLuffy Feb 23 '17 at 10:08
  • @Dominik You have exactly right, I try it and I have the warning. BUT, it don't work, because the cast give me FirstClass.class or SecondClass.class but not the type of interface (FirstListener.class and SecondListener.class) :( – MonkeyJLuffy Feb 23 '17 at 10:27

2 Answers2

2

Usually, you would just declare for every listener type two methods:

public void addFirstListener(MyFirstListener listener) {
    listenerList.add(MyFirstListener.class, listener);
}

public void removeFirstListener(MyFirstListener listener) {
    listenerList.remove(MyFirstListener.class, listener);  
}

If you want to be able to add and remove listeners dynamically, you will have to lookup the type of the listener which should be added/removed by its runtime type.

For example, create a list of supported listener types. In your example for the two types MyFirstListener.class and MySecondListener.class:

private final static List<Class<? extends EventListener>> SUPPORTED_LISTENER_TYPES 
        = Arrays.asList(MyFirstListener.class, MySecondListener.class);

Then you will need a method that is able to lookup the corresponding listener type for a given listener:

private Class<? extends EventListener> getListenerType(EventListener listener){
    for(Class<? extends EventListener> listenerType : SUPPORTED_LISTENER_TYPES){
        if(listenerType.isAssignableFrom(listener.getClass())){
            return listenerType;
        }
    }
    return null;
}

Now, you can use this method inside your addListener and removeListener method:

@SuppressWarnings("unchecked")
public void addListener(EventListener listener) {
    if(listener == null){
        return;
    }
    Class<? extends EventListener> listenerType = getListenerType(listener);
    if(listenerType == null){
        throw new IllegalArgumentException("Listener " + listener + " not supported");
    }
    listenerList.add((Class<EventListener>)getListenerType(listener), listener);
}

The unchecked cast here is safe, because the given listener is guaranteed to be a descendant of the type returned by getListenerType().

Calculator
  • 2,769
  • 1
  • 13
  • 18
  • Yeah you have right. After your responses, I try my code with the solution of @Dominik, but the getClass() return to me FirstClass.class or SecondClass.class but not the type of interface (FirstListener.class and SecondListener.class). Then I will implement the normal way with addListeners for each kind. – MonkeyJLuffy Feb 23 '17 at 10:49
  • An another way to have the same result is this implementation but not so generic : ` public void addListener(T listener) { if (listener instanceof MyFirstListener) { listenerList.add(MyFirstListener.class, (MyFirstListener)listener); } else if (listener instanceof MySecondListener) { listenerList.add(MySecondListener.class, (MySecondListener)listener); } else { System.err.println("Wrong listener"); } } ` – MonkeyJLuffy Feb 23 '17 at 10:52
0

You can't refer a class this way. Call listener.getClass instead.

Egor Zhuk
  • 274
  • 1
  • 7
  • getClass() will return neither `MyFirstListener.class` nor `MySecondListener.class`. – VGR Feb 22 '17 at 17:45