Reputation: 22977
I ran into a problem regarding Java generics.
I'm trying to make a listener (called SomeEventListener
) which listens to SomeEvent
s to occur. Those events have a result which have a specific type; I want that result to be retrieved through the method getResult()
, whose return type is T
. When that event is fired, then the eventOccurred(SomeEvent<T>)
method is called.
SomeEvent class:
public class SomeEvent<T> {
private T result;
public SomeEvent(T result) {
this.result = result;
}
public T getResult() {
return this.result;
}
}
SomeEventListener class:
public interface SomeEventListener<T> {
void eventOccurred(SomeEvent<T> event);
}
AnotherClass class
public class AnotherClass {
ArrayList<SomeEventListener<?>> listeners = new ArrayList<SomeEventListener<?>>();
<T> void fireSomethingOccursEvent(SomeEvent<T> event) {
for (SomeEventListener<?> listener : this.listeners) {
listener.eventOccurred(event); // MARKED LINE
}
}
}
But hey, Eclipse gives an error on the MARKED LINE:
The method eventOccurred(SomeEvent<capture#2-of ?>) in the type SomeEventListener<capture#2-of ?> is not applicable for the arguments (SomeEvent<T>)
I have suspects why this occurs – it possibly has something to do with generics and type erasure – but I cannot solve it.
My questions:
Notice: Posts about generics and/or type erasure are plenty; however, I couldn't find the answer to this question. If you think another answer is suitable, please link to it.
Upvotes: 0
Views: 116
Reputation: 12939
You can make whole AnotherClass
generic and use wildcards:
public class AnotherClass<T> {
ArrayList<SomeEventListener<? super T>> listeners = new ArrayList<>();
//use diamond-type since java 1.7
void fireSomethingOccursEvent(SomeEvent<? extends T> event) {
for (SomeEventListener<? super T> listener : this.listeners) {
listener.eventOccurred(event); // should be fine
}
}
}
Upvotes: 0
Reputation: 58868
You have a SomeEventListener<?>
, with a method void eventOccurred(SomeEvent<?> event)
(substituting ?
as T
).
You also have a SomeEvent<T>
for some completely unrelated T
.
You can't convert this to a SomeEvent<?>
- in fact, you can't convert anything to a SomeEvent<?>
except for null
. ?
means "I have no idea what this type argument is."
As an example, if your code was allowed, this code would compile, but pass the wrong type:
AnotherClass ac = new AnotherClass();
ac.listeners.add(new SomeEventListener<Integer>() {
void eventOccurred(SomeEvent<Integer> event) {
System.out.println(event.intValue());
}
});
SomeEvent<String> event = new SomeEvent<String>("Hello");
ac.fireSomethingOccursEvent(event);
Now fireSomethingOccursEvent
would take the SomeEvent<String>
and pass it to a SomeEventListener<Integer>
. This obviously should not be allowed - so, it's not allowed.
Upvotes: 2