hertzsprung
hertzsprung

Reputation: 9913

Mixing nested type parameters and wildcards in Java

Why does trying to compile

public class GenericsFail {
    public static void main(String[] args) {
        accept(new HashMap<String, List<String>>());
    }

    public static void accept(Map<String, List<?>> multiMap) {}
}

give the error

GenericsFail.java:7: error: method accept in class GenericsFail cannot be applied to given types;
                accept(new HashMap<String, List<String>>());
                ^
  required: Map<String,List<?>>
  found: HashMap<String,List<String>>
  reason: actual argument HashMap<String,List<String>> cannot be converted to Map<String,List<?>> by method invocation conversion

The wildcard is only allowed if it's not nested inside List.

Upvotes: 14

Views: 1571

Answers (3)

Bohemian
Bohemian

Reputation: 425358

The reason is that the ? in List<?> could be "anything", but a different "anything" in each Map entry. That is, it would accept a List<String> in one entry, and a List<Integer> in another.

But you are passing in a Map that has the same type of List in every entry, so the type is not bound in the same way or the to same degree for freedom.

The "fix" is to lock the type to a specific type, but still being "anything" - just the same "anything* in every entry, by typing the method:

public static <T> void accept(Map<String, List<T>> multiMap) // complies

or if your method really doesn't need to know which type, use a wildcard to wrap the type:

public static void accept(Map<String, ? extends List<?>> multiMap) // compiles

This last version works because the type of the list, although being a wildcard, is fixed to an unknown, but consistent, type when called.


I find the typed version easier to read (and code), and the type is there for use should you decide later that your method needs to know the type.

Upvotes: 14

irreputable
irreputable

Reputation: 45453

To be more general

void accept(Map<String, ? extends List<?>> multiMap)

Upvotes: 4

Pierre-Henri
Pierre-Henri

Reputation: 1505

Here is a short example of why the compilator cannot accept this parameter :

    public static void main(String[] args) {
        //if this would be accepted here ...
        accept(new HashMap<String, List<String>>());
    }

    public static void accept(Map<String, List<?>> multiMap) {
        //this is valid considering the type of multiMap parameter,
        //but would not work with the actual passed parameter.
        multiMap.put("test", new ArrayList<Integer>());
    }

Upvotes: 2

Related Questions