Following on from this question, which provides a solution but doesn't explain it (unfortunately, the links in the answers are now dead):
Take the following method:
void method(Map<?, ?> myMap) {
Set<Map.Entry<?, ?>> set = myMap.entrySet();
...
}
Simple, no? However, this fails to compile on jdk1.7.0_25:
incompatible types required: java.util.Set<java.util.Map.Entry<?,?>> found: java.util.Set<java.util.Map.Entry<capture#1 of ?,capture#2 of ?>>
WTF? Map.entrySet()
is specified as returning an object of type Set<Map.Entry<K, V>>
, so in the example above, myMap.entrySet()
returns a Set<Map.Entry<?, ?>>
. But it doesn't compile!
Even weirder, from the linked question at the top, changing the method to this makes it compile:
void method(Map<?, ?> myMap) {
Set<? extends Map.Entry<?, ?>> set = myMap.entrySet();
...
}
WTF??? Calling entrySet
on a Map<?, ?>
returns a Set<Map.Entry<K, V>>
, which can't be assigned to a variable of type Set<Map.Entry<K, V>>
, but it can to a variable of type Set<? extends Map.Entry<K, V>>
?????
Can anyone shed light on what's going on here? And does this mean that, whenever I write a method using a wildcard type at least 2 levels deep, I have to remember to make it ? extends ...
somewhere?
Each of those ? can vary independently, so there's no guarantee that the
<?,?>
in the declaration ofmyMap
matches the<?,?>
in the declaration ofset
.What this means is that once I have a
Set<Map<?,?>>
, I can put any type ofMap
into that set, becauseMap<?,?>
is a supertype of all types ofMap
. But this is not a property thatSet<Map<String,Integer>>
(for example) has - it's far more restrictive in terms of what types of map I can put into it. SoSet<Map<?,?>>
is not a supertype ofSet<Map<String,Integer>>
. ButmyMap.entrySet()
could easily be aSet<Map<String,Integer>>
, depending on whatmyMap
is. So the compiler has to forbid us from assigning it to a variable of typeSet<Map<?,?>>
, and that's what's happening.On the other hand,
Set<? extends Map<?,?>>
is a supertype ofSet<Map<String,Integer>>
, becauseMap<String,Integer>
is a subtype ofMap<?,?>
. So it's OK to assignmyMap.entrySet()
to a variable of typeSet<? extends Map<?,?>>
.Note that there's nothing special about
String
andInteger
here, butmyMap
has to be a map of something!You could write