// I know that this method will generated duplicate
// trim keys for the same value but I am just
// trying to understand why we have a compile error:
// The method
?
is not a wildcard for any type but the unknown type. So the map only accepts ?
-type objects and not String
. One conclusion from this (rather strange) fact: We can't add values to collections that are parametized with ?
.
You cannot add values (that are not null
) to Collection
s and Maps
parametrized with a wildcard ?
. That is because a wildcard is not a substitute for any object, it's just an unknown object. So you cannot add any (lets put it that way) known object in a map that accepts only unknown.
You can only read values.
It might have been possible for the Java 5 expert group, to add a little more power to the specification of wildcards in generic method argument types. I suspect the enhancement didn't make it due to lack of time in the specs phase. See my own recent question here: How does the JLS specify that wildcards cannot be formally used within methods?
Short of a better solution, you have to make your method a generic method:
<V> void trimKeyMap(Map<String, V> map){
for (String key : map.keySet()) {
map.put(StringUtils.trim(key), map.get(key));
}
}
You cannot put anything in a Map<String, ?>
type, except for the null
literal:
Map<String, ?> map = new HashMap<String, String>();
map.put("A", null); // Works
map.put("B", "X"); // Doesn't work.
The compiler doesn't know that the map's value argument type was String
. So it cannot allow you to add anything to the map, even if you got the value from the map itself.
The problem is that the compiler only knows the key type is "unknown", but doesn't know it's the same unknown type for the type of the Map's key and the type returned from get()
(even though we as humans realize that it's the same).
If you want to make it work, you must tell the compiler it's the same unknown type by typing your method, for example:
void <V> trimKeyMap(Map<String, V> map) {
for (String key : map.keySet()) {
map.put(StringUtils.trim(key), map.get(key));
}
}
You can use type inference in a helper method, if you want to keep the clean method signature (the client of trimKeyMap shouldn't have to use a generic method):
void trimKeyMap(final Map<String, ?> map) {
for (final String key : map.keySet()) {
trim(map, key);
}
}
private <T> void trim(final Map<String, T> map, final String key) {
map.put(StringUtils.trim(key), map.get(key));
}
This is called wildcard capture and is discussed more here: http://www.ibm.com/developerworks/library/j-jtp04298/
?
is different from Object
.
A Map<String, ?>
is a generic map where you don't know the element type, but it is still being enforced, i.e. it does not allow you to put anything.