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
.
Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.
In the Java programming language, the wildcard ? is a special kind of type argument that controls the type safety of the use of generic (parameterized) types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type.
In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific).
Wildcards in Java are basically the question marks which we use in generic programming, it basically represents the unknown type. We use Java Wildcard widely in situations such as in a type of parameter, local variable, or field and also as a return type.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With