I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
It's easy to understand if you remember that
Collection<Object>
is just a generic collection that contains objects of typeObject
, butCollection<?>
is a super type of all types of collections.Another way to think about this problem is that
is equivalent to
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
and the wild card may start making more sense, hopefully.
An instance of
HashMap<String, String>
matchesMap<String, ?>
but notMap<String, Object>
. Say you want to write a method that accepts maps fromString
s to anything: If you would writeyou can't supply a
HashMap<String, String>
. If you writeit works!
A thing sometimes misunderstood in Java's generics is that
List<String>
is not a subtype ofList<Object>
. (ButString[]
is in fact a subtype ofObject[]
, that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).Sample: If you'd like to write a method that accepts
List
s ofInputStream
s and subtypes ofInputStream
, you'd writeBy the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Declaring
hash1
as aHashMap<String, ?>
dictates that the variablehash1
can hold anyHashMap
that has a key ofString
and any type of value.All of the above is valid, because the variable
map
can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the
map
variable:All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside
map
is.You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of
Object
and, so, whatever you get out of the map will be of the type Object:The above block of code will print 10 to the console.
So, to finish off, use a
HashMap
with wildcards when you do not care (i.e., it does not matter) what the types of theHashMap
are, for example:Otherwise, specify the types that you need:
In the above method, we'd need to know that the Map's key is a
Character
, otherwise, we wouldn't know what type to use to get values from it. All objects have atoString()
method, however, so the map can have any type of object for its values. We can still print the values.You can't safely put anything into
Map<String, ?>
, because you don't know what type the values are supposed to be.You can put any object into a
Map<String, Object>
, because the value is known to be anObject
.