How to add values for java generic map with undetermined "?" value type

Viewed 1931

I've seen this kind of declaration in jdk 8 sample:

    Map<String, ?> map = new HashMap<>(3);//OK

But when I tried to add value to "map", I didn't succeed:

    map.put("abc", Optional.of(5));
    map.put("kk", "xyz");

Both fail to compile. I wish to know:

(1) What does "?" indicate in Map declaration above?

(2) How to give values to this "map"?

3 Answers

Map<String,?> is an abstract type. If a variable has this type, it could reference an object with any of the following types (and others).

  • HashMap<String,Integer>
  • TreeMap<String,String>
  • HashMap<String,ArrayList<Boolean>>
  • TreeMap<String,Throwable>

Obviously, the possibilities are basically endless. If you have a variable of this type, you know that it refers to a map whose keys are String, but you really know nothing else. In particular, you don't know what type of object you'll end up with when you do a get on that Map.

More importantly though, you will never be able to put anything into the map, without some kind of nasty, unsafe casting operation. The compiler will stop you. So in the examples you've given -

  • map.put("abc", Optional.of(5)); won't compile, because map could be a HashMap<String,String>, into which you can't put an Optional.
  • map.put("kk", "xyz"); won't compile, because map could be a TreeMap<String,Integer>, into which you can't put a String.

The exceptions would be null, or any value that has come from the map itself - see Andy Turner's excellent answer for more detail about those possibilities.

In short, if you have a variable of type Map<String,?>, the operations that the compiler will let you do to it are a bit limited. You can't put anything into the map, unless it's null or it's already in the map. All you can do is get values from the map, and remove values from the map.

So using a Map<String,?> variable is very limiting. If all you want to do with the map is read values from it, this is just fine, of course. But don't expect to be able to insert arbitrary values into the map, unless you use a different expression to refer to the map.

Question (1)

? is called wildcard generic type.It can be used with any type,
but you need to specify type fist as Map is an abstract class.

Question (2)

for you to give value to this map its either you use Upper bound or Lower bound.

The following are guidelines when using upper bound or lower bound

? extends Type   - is used for read access only
? super Type     - is used for write access only.

PECS in short Produces(write acccess) -uses Extends while Consumes(read access) - uses Super

Map<String, ? super Object> map = new HashMap<>(3);//OK
    map.put("abc",Optional.of(5));
    map.put("kk","xyz");
map.forEach((k,v)->System.out.println(k+"\t"+v));

Output

kk  xyz
abc Optional[5]

Process finished with exit code 0

Additional Notes

Unbounded wildcard ?

 List<?> l = new ArrayList<String>();

Wildcard with an upper bound ? extends type

 List<? extends Exception> l = new ArrayList<RuntimeException>();

Wildcard with a lower bound ? super type

 List<? super Exception> l = new ArrayList<Object>();

? is an unknown type: you don't know exactly what it is at that point in the code.

Because of that, you cannot safely add any value to the map, with a couple of exceptions.

You can add literal null (because null can be cast to any type):

map.put("abc", null); // compiles

Also, you can add a value to the map which you obtain from the map:

<T> void reAdd(Map<String, T> map) {
  T value = map.get("123");
  map.put("456", value);
}

// Call with
reAdd(map); // compiles
Related