I have to place an integer value into the map below.
Map<String,?> map
map.put("key",2000);
When I run the above code I'm getting the following error:
incompatible types: java.lang.Integer cannot be converted to capture#1 of ?
I have to place an integer value into the map below.
Map<String,?> map
map.put("key",2000);
When I run the above code I'm getting the following error:
incompatible types: java.lang.Integer cannot be converted to capture#1 of ?
You can assign many things to a a variable of type Map<String, ?>
. For example:
Map<String, ?> map;
map = new HashMap<String, Object>(); // works
map = new HashMap<String, String>(); // works
map = new HashMap<String, Number>(); // works
map = new HashMap<String, Integer>(); // works
That all works. So, let's say that map = new HashMap<String, String>()
was assigned to it. And then you attempt to run .put("key", 2000);
on it.
That, obviously, makes no sense. Hence why that code doesn't compile.
I'm assuming that your eyeballs and human brain looked at this code and determined that the map
you have there couldn't possibly be pointing at anything other than either a Map<String, Object>
or a Map<String, Number>
or a Map<String, Integer>
or even a Map<String, Serializable>
- types for which .put("key", 2000)
would be sensible.
But that's not how java works. Part of the point of types is to restrict yourself and give yourself the freedom to assign other stuff later.
It is exactly analogous to this:
Object x = "hello";
x.toLowerCase();
The above does not compile: The Object
type doesn't have a toLowerCase()
method. Yes, yes, our eyeballs and brain look at this and go: That's stupid, of course it does! Look, x
is clearly pointing at a string!
But in such cases, just.. write String x = "hello"
.
So, the same applies for your scenario. Just write Map<String, Integer>
instead.
sometimes you have dynamic code that wants to check what a thing is and act accordingly. For example:
void foo(Object o) {
if (o instanceof String s) return s.toLowerCase();
throw new SomeException();
}
But you can't do that for generics - generics cannot be instanceof
checked. You can't do something like: IF the object my map
variable is pointing at has Integer
as value type (or any supertype of Integer), then do map.put("key", 2000)
, otherwise don't.
That's just not how generics works in java. There is simply no way to do this.
The only thing you can do is just decree it. Write code that works fine if it is true, but messes everything up if it is not true: You can FORCIBLY run .put("key", 2000)
on the map, which will put that in the map, and if that is a map declared as, say, Map<String, String>
, then any code that interacts with this map will start throwing ClassCastExceptions
left and right even though they never cast anything. Because you shoved an integer into a map of strings.
Hence why the compiler yells at when you force this, and why you shouldn't be doing this. The only point of generics is to make your programming life easier, to clarify APIs, and to catch bugs, and you're.. doing the opposite of it by forcing it. But, hey, if you really want to do this (highly unlikely):
void example(Map<String, ?> map) {
Map forceIt = map; // raw assignment
forceIt.put("key", 2000); // compiles
}
The above compiles and does what you asked for - but will warn you that this is a very bad idea.
of (o instanceof String) return ((String) o).toLowerCase();
Unbounded generic collections (
Collection<?>
) are not writable. Let's explore why.Unknown type - <?>
A question mark in angle brackets
<?>
is called the unknown type. For the compiler, that means that the actual type can't be predicted and at runtime it could appear to be pretty match anything: anObject
, aString
, aCat
. Therefore, everything that you retrieve from the unbounded collection will be treated as anObject
:But don't confuse an unbounded collection
Collection<?>
with a collection of objectsCollection<Object>
. They are incompatible.Collection<Object>
has invariant behavior, i.e. it expects only another collection of typeObject
to be assigned to it. Meanwhile, we can assign a collection of any type toCollection<?>
.So, unknown type could be represented by anything that extends
Object
. And you might think aboutCollection<?>
as if it is a upper-bounded collectionCollection<? extends Object>
. They are absolutely compatible.Modifying an unbounded collection
Wild-cards like
<?>
or<? extends Object>
are not actual types. They are just a way to tell the compiler that we don't know what the type will be at runtime.Then what will be the actual type of
Collection<?>
?Since it's already clear that unknown type (
<?>
) implies that it could be any type that extendsObject
, hence under the hood of aCollection<?>
could appear anArrayList<Object>
, anArrayList<String>
, aHashSet<BigDecimal>
, etc.Remainder: parameterized collection that will appear in the guise of
Collection<?>
at runtime is invariant, which means thatArrayList<String>
is expected to contain onlyString
s andHashSet<BigDecimal>
is expected to store only instances ofBigDecimal
. But that there's no way to ensure that at runtime because during the compilation generic parameters get erased. Therefore, the only way to provide type-safety while utilizing unbounded generic collection is to disallow to add anything into it.For example, since it's illegal to add an
Integer
or anObject
into anArrayList<String>
, or to add an instance ofString
into aHashSet<BigDecimal>
compiler will prevent any attempt to add anything to theCollection<?>
. Because there's no type compatible with the unknown type.There's only one exception,
null
is a valid value for any type of object, and therefore it could be added into an unbounded as well as into an upper-bounded collection.Will give an output:
Note: that there are no restrictions on removal of elements for both unbounded and upper-bounded collections.
Alternatives
Hence, there's no way to add new entries to the unbounded map you can either copy its entries one by one performing an
instanceOf
check into the regular generic mapMap<String, Integer>
get rid of generics at all by assign the map to a variable of row type, and then add type-casts manually when need to retrieve a value like in Java 1.4.That how you can copy the contents of unbounded map to a regular and utilize the advantages of generics.
The same thing by using the Stream API