I get error on second line, isn't all items an Object
?
Map<String, ?> value; // I get value from some place
Map<String, Object> val = value;
I get error on second line, isn't all items an Object
?
Map<String, ?> value; // I get value from some place
Map<String, Object> val = value;
The ?
in generics (in Java land) denotes that it can be substituted for any type (this type may be optionally bounded.) In the case of generics, Object
cannot safely be substituted for any type this way (it runs into the problem of generics and subtyping, because generics are erased at runtime.)
Thus, the two are not semantically identical as you might first expect, hence the compilation error.
As an example of what could go wrong if this were allowed, you could do:
Map<String, ?> value = new HashMap<String, String>();
Map<String, Object> val = value; //This line would fail in real life, but assuming it passes...
val.put("Hello", 0); //We could do this!
...which clearly doesn't make any sense - the HashMap
is of type <String, String>
yet you've put an Integer
in it!
This is where the ?
representation differs - you could not put anything other than an Object
in the map without causing a compile error:
value.put("Hello", 0); //Error
It's worth noting that arrays do not carry these same restrictions, since they are reified and thus can throw meaningful exceptions at runtime when a type mismatch occurs. (Whether this is preferable is a matter of debate, but personally I much prefer catching errors early at compile time!) Since generics can't do this, the compiler has to ensure their safety at compile time.
No. Map<String, Object>
is not a supertype of Map<String, ?>
, so that assignment isn't type-safe.
(However, Map<String, ?>
is the supertype of all Map<String, T>
. So the opposite of your code would work.)
Both Map<String, ?>
and Map<String, Object>
could potentially take any Object
as value. As far as you know in your code that's the most specific you can assume. (relevant for .get(key)
)
But the placeholder ?
means that it can be a different concrete type.
E.g. Map<String, ?>
is allowed to be a Map<String, Apple>
at runtime. And you can not assign generics with different types to each other. Not even when there is just a theoretical chance that you could have a map of apples.
No, it's a capture of
and that causes a compile error, because obviously ?
doesn't just mean Object... however, this is what you seem to want -
Map<String, Object> value; // I get an Object
Map<String, ? extends Object> val = value; // Something that is an Object.
or even
Map<String, Object> value;
Map<String, ?> val = value; // Will take anything...
// Object is more specific.
With a wildcard bound ?
, you're telling the compiler that you don't know and don't care what the generic type is. This is fine if you're only reading from the container and using methods declared on the upper bound (for example, you can read from a Collection<?>
and call toString()
, or a List<? extends Closeable>
and call close()
), but it's not safe to then place objects into the collection (it might really be a Collection<URL>
, and trying to add a String
would corrupt it).
I am an idiot and am leaving this here for my shame.
Covariance and contravariance with generic types in c# is a common source of confusion. It was sort-of-fixed (not that it was broken per se) in .NET 4, but the new way of making generic types work the way you describe is opt-in, not retroactive.
Map<String, ?> value = null; // initialize
Map<String, Object> val = (Map<String, Object>) value; //cast