18

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.

forsvarir
  • 10,749
  • 6
  • 46
  • 77
mmoossen
  • 1,237
  • 3
  • 21
  • 32

5 Answers5

19

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.

informatik01
  • 16,038
  • 10
  • 74
  • 104
Ben Lings
  • 28,823
  • 13
  • 72
  • 81
13

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>
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 6
    String class is final, so just Map – nybon Jun 02 '10 at 05:49
  • 1
    @nybon Suppose you have class A extends Map and class B extends Map, you want to write a method that can take either class A or class B as argument. That's when you need Map – Truong Oct 25 '21 at 05:10
6

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.
Hardcoded
  • 6,476
  • 2
  • 22
  • 20
0

Try this:

public Map<String, Object> getData();
Juha Syrjälä
  • 33,425
  • 31
  • 131
  • 183
-2

[EDIT] This is really wrong... I understood.

My first answer was:

That's java : String is not an object.

Try with

obj.getData().put("key",new String("value"));

But String extends Object... while I thought String was a primitive. I learned something ^^

enguerran
  • 3,193
  • 3
  • 26
  • 42
  • 3
    A string certainly *is* an object. What precise difference do you believe it would make to call the String constructor explicitly in this case? – Jon Skeet Dec 17 '09 at 11:55
  • String is not a primitive type, is it? – enguerran Dec 17 '09 at 11:58
  • No, it's not a primitive type... so why did you claim it's not an object? – Jon Skeet Dec 17 '09 at 11:58
  • I believed it was a primitive, like a almost object... Mistaken! But what is the difference between my thought and Ben Lings one? – enguerran Dec 17 '09 at 12:00
  • 1
    Ben Lings' answer is *completely* different, and accurate. What do you think the similarity is between your answer and Ben's? – Jon Skeet Dec 17 '09 at 12:08
  • He 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(), I can't do anything else than list.add(null)... My brain cannot get it... – enguerran Dec 17 '09 at 12:23
  • OK, 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. – enguerran Dec 17 '09 at 12:49