0

I don't understand why the compiler doesn't accept this code

import javax.swing.event.EventListenerList;

import java.util.EventListener;


public class GenericEventsManager {

   EventListenerList listeners = new EventListenerList();


   public <T extends EventListener> void addListener(T listener) {
      listeners.add(listener.getClass(), listener);
   }
}

The error I get is

The method add(Class<T>, T) in the type EventListenerList is not applicable for the arguments (Class<capture#1-of ? extends EventListener>, T)

The argument in addListener is of a type that extends EventListener, so listener.getClass() returns Class<? extends EventListener>, which is exactly what the EventListenerList.add method expects

Can someone explain this? I have a feeling that it has something to do with getClass() not being resolved at compile time but it still doesn't make sense to me

barik
  • 160
  • 2
  • 12
Hilikus
  • 9,954
  • 14
  • 65
  • 118

2 Answers2

2

Generic parameters without wildcards are not variant and therefore require exactly the same types (not sub-types, not super types). (source)

The compiler simply will not let you do this. As the error message implies, the type parameter on the Class parameter must be exactly the same as the type of the second argument, which is not true in the code above.


How can they not be identical?

Because the return type of listener.getClass() is Class<? extends EventListener>, not Class<T>. You could — but I do not recommend doing so — cast the returned Class to make this compile:

listeners.add((Class<T>)listener.getClass(), listener);

You'll get a compiler warning when doing so:

Type safety: Unchecked cast from Class<capture#1-of ? extends EventListener> to Class<T>

because this is not a (type)safe cast.


The root cause of this might (I'm honestly not sure) be simply poor API design in the declaration of EventListenerList#add(). Typically PECS is used in this kind of situation; it's possible there's a good reason that add() is not wildcarded.

Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • How can they not be identical? – Hilikus May 16 '12 at 17:20
  • I see. Why can't the compiler go one step further and figure out that T **is** "? extends EventListener"? – Hilikus May 16 '12 at 17:27
  • Because that's not how `Object#getClass()` is declared. Note that you cannot write `Class clazz = listener.getClass();` or `Class clazz = listener.getClass();` because those will also not compile. – Matt Ball May 16 '12 at 17:30
  • Read this closely for another example: http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html – Matt Ball May 16 '12 at 17:36
0

Yes but in your add method you are specifying a Class<T>, not a Class<EventListener>

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
ControlAltDel
  • 33,923
  • 10
  • 53
  • 80