3

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?

Community
  • 1
  • 1
thecoop
  • 45,220
  • 19
  • 132
  • 189

1 Answers1

6

Each of those ? can vary independently, so there's no guarantee that the <?,?> in the declaration of myMap matches the <?,?> in the declaration of set.

What this means is that once I have a Set<Map<?,?>>, I can put any type of Map into that set, because Map<?,?> is a supertype of all types of Map. But this is not a property that Set<Map<String,Integer>> (for example) has - it's far more restrictive in terms of what types of map I can put into it. So Set<Map<?,?>> is not a supertype of Set<Map<String,Integer>>. But myMap.entrySet() could easily be a Set<Map<String,Integer>>, depending on what myMap is. So the compiler has to forbid us from assigning it to a variable of type Set<Map<?,?>>, and that's what's happening.

On the other hand, Set<? extends Map<?,?>> is a supertype of Set<Map<String,Integer>>, because Map<String,Integer> is a subtype of Map<?,?>. So it's OK to assign myMap.entrySet() to a variable of type Set<? extends Map<?,?>>.

Note that there's nothing special about String and Integer here, but myMap has to be a map of something!

You could write

<K, V> void method(Map<K, V> myMap) {
    Set<Map.Entry<K, V>> set = myMap.entrySet();
    ...
Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
  • I don't understand. It's a wildcard, so it doesn't matter what it is - the compiler can make the `?`s in `set` the same as the `?` returned from `entrySet`, _because they're both wildcards_. – thecoop Nov 01 '13 at 13:16
  • Your answer isn't wrong, but I don't think that it's obvious why each of the wildcards varying separately means that the wildcarded `myMap`'s entry set method returns `Set>` and not `Set>`. – Matt Ball Nov 01 '13 at 13:16
  • Also, my actual problem is more complicated than this - the return value of `entrySet` is being passed to a method parameter of type `Collection>`. Fortunately, I can change the method being called... – thecoop Nov 01 '13 at 13:17
  • No, the compiler can't make the `?`s into whatever it likes. `Map,?>` is a supertype of every type of Map, and `Map.Entry,?>` is a supertype of every type of Map.Entry. So you could easily make them incompatible types. This is how the compiler stops you from doing that. – Dawood ibn Kareem Nov 01 '13 at 13:18
  • @David Isn't that what his fix does anyway? – Deadron Nov 01 '13 at 13:21
  • @MattBall - You're right, it wasn't obvious. I hope the new paragraph that I've just added clarifies it a bit. – Dawood ibn Kareem Nov 01 '13 at 13:27
  • @Deadron - I guess so, but I thought that OP was really after an explanation more than anything else. After all, he/she did find a fix, even though it was different from my fix. – Dawood ibn Kareem Nov 01 '13 at 13:33