span
span

Reputation: 5624

Interface with generic method parameter type in list

I have a HashMap that links specific RequestTypes to separate LinkedLists. The lists consists of an interface with a generic type. I have no problems adding to the lists in the map but I can't seem to get the lists out of the map.

I'll show you two of my tries and there corresponding errors. First I'll show you the interface and my Map when I want to call the method in the interface.

public interface IRequestListener<Result> {
    public void resultUpdated(Result result);
}

private HashMap<RequestType, LinkedList<IRequestListener<?>>> requestListenerMap = 
    new HashMap<RequestType, LinkedList<IRequestListener<?>>>();

In the following code, RequestType and Notification are two simple enums.

Here is the first try:

Notification notification = Notification.AVOID;
LinkedList<IRequestListener<?>> listeners = 
    requestListenerMap.get(RequestType.NOTIFICATION);
for(IRequestListener<?> listener : listeners) {
    listener.resultUpdated(notification); // ERROR ON THIS LINE
}

That causes the following error:

The method resultUpdated(capture#1-of ?) in the type 
IRequestListener<capture#1-of ?> is not applicable for 
the arguments (Notification)

Here is the second try:

Notification notification = Notification.AVOID;
LinkedList<IRequestListener<Notification>> listeners = 
    requestListenerMap.get(RequestType.NOTIFICATION); // ERROR ON THIS LINE
for(IRequestListener<Notification> listener : listeners) {
    listener.resultUpdated(notification);
}

That causes the following error:

Type mismatch: cannot convert from LinkedList<IRequestListener<?>> 
to LinkedList<IRequestListener<Notification>>

I'm thinking that I'm getting tripped up by the inheritance/casting issues that are tricky with generics but I can't figure out how. I don't want to extend Notification since Result in the interface can be either a Notification or an Integer at this time. At a later time I might also add the possibility to have a List as a Result.

Cheers.

Upvotes: 0

Views: 246

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500215

It sounds like you want to constrain your Result type parameter to extend Notification:

private HashMap<RequestType, LinkedList<IRequestListener<? extends Notification>>> 
    requestListenerMap = new HashMap<>(); // Assuming Java 7

...

LinkedList<IRequestListener<? extends Notification>> listeners = 
    requestListenerMap.get(RequestType.NOTIFICATION);
for(IRequestListener<? extends Notification> listener : listeners) {
    listener.resultUpdated(notification);
}

Now if that's not appropriate for the map declaration - because you'd want to store other lists for other entries - you may need an unsafe cast:

private HashMap<RequestType, LinkedList<IRequestListener<?>>> requestListenerMap = 
    new HashMap<RequestType, LinkedList<IRequestListener<?>>>();

...

LinkedList<IRequestListener<?>> listeners = 
    requestListenerMap.get(RequestType.NOTIFICATION);
for (IRequestListener<?> listener : listeners) {
    // Note that this cast is unsafe.
    IRequestListener<? extends Notification> notificationListener = 
        (IRequestListener<? extends Notification>) listener;
    notificationListener.resultUpdated(notification);
}

Fundamentally you can't do this safely, as the execution-time type won't include the type argument. But you'll get a ClassCastException when you call resultUpdated if it's inappropriate.

Upvotes: 1

Related Questions