Problem using generic map with wildcard
Solution 1
The wildcard means "the value type parameter could be anything" - it doesn't mean "you can use this as if it were anything you want it to be". In other words, a Map<String, UUID>
is valid as a Map<String, ?>
- but you wouldn't want to be able to put a String value into it.
If you want a map which can definitely accept string values, you want:
Map<String, ? super String>
Solution 2
The return type of
Map<String, ?>
is the same as
Map<String, ? extends Object>
The means that the concrete type returned could be a Map<String, AnyClass>
. You can't put a String
into an AnyClass
, hence the error.
A good general principle is to not use wildcards in method return types.
Solution 3
Map<String, ?>
is a short form of Map<String,? extends Object>
and doesn't mean that anything can be added as value. It says that the Map-object can have any generic value type extending Object
.
This means that the Map object can be a HashMap<String, String>
or a HashMap<String, Integer>
as well. Because the compiler can't check which value types will be accepted, he won't let you call methods with the value type as a parameter.
Note:
- You can call methods with the value type as a return value, because everything must extend Object (? extends Object)
- A
Map<String, ? super String>
will have the opposite effect: You can always use a String as parameter, but the return-type is unclear.
Related videos on Youtube
mmoossen
Updated on April 15, 2022Comments
-
mmoossen about 2 years
I have a method that returns a
map
defined as:public Map<String, ?> getData();
The actual implementation of this method is not clear to me, but, when I try to do:
obj.getData().put("key","value")
I get following compile time error message:
The method put(String, capture#9-of ?) in the type Map is not applicable for the arguments (String, String)
What is the problem? Is
String
not of type anything?Thanks in advance.
-
Jon Skeet over 14 yearsA string certainly is an object. What precise difference do you believe it would make to call the String constructor explicitly in this case?
-
enguerran over 14 yearsString is not a primitive type, is it?
-
Jon Skeet over 14 yearsNo, it's not a primitive type... so why did you claim it's not an object?
-
enguerran over 14 yearsI believed it was a primitive, like a almost object... Mistaken! But what is the difference between my thought and Ben Lings one?
-
Jon Skeet over 14 yearsBen Lings' answer is completely different, and accurate. What do you think the similarity is between your answer and Ben's?
-
enguerran over 14 yearsHe told us than Map<?> is the same as List<? extends Object>. And he told than AnyClass, as long as AnyClass extends Object, can't be a String. I did some tries: if I write List<?> list = new ArrayList<Object>(), I can't do anything else than list.add(null)... My brain cannot get it...
-
enguerran over 14 yearsOK, it seems I will not have my explanation... I get that List<? extends Object> is a read-only List. I get too that List<? super Object> is the correct answer. But I cannot get why this and that.
-
nybon almost 14 yearsString class is final, so just Map<String, String>
-
Misa over 2 years@nybon Suppose you have class A extends Map<String, String> and class B extends Map<String, Object>, you want to write a method that can take either class A or class B as argument. That's when you need Map<String, ? super String>