MC Emperor
MC Emperor

Reputation: 22977

Method not applicable for arguments regarding generics or type erasure in Java

I ran into a problem regarding Java generics.

I'm trying to make a listener (called SomeEventListener) which listens to SomeEvents 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

Answers (2)

kajacx
kajacx

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

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

Related Questions