I am wondering what is wrong with this code:
Map <? extends String, ? extends Integer> m = null;
Set<Map.Entry<? extends String, ? extends Integer>> s = m.entrySet();
The compiler complains with the error message:
Type mismatch: cannot convert from
Set<Map.Entry<capture#1-of ? extends String,capture#2-of ? extends Integer>>
toSet<Map.Entry<? extends String,? extends Integer>>
What should the type of s
be? Eclipse suggests Set<?>
but I am trying to get more specific than that.
This issue is addressed in this old Apache thread:
The crux of the problem is that top-level wildcards capture, meaning they are essentially one-off type parameters. In contrast, nested wildcards don't capture, and have somewhat of a different meaning.
So, removing the bounds for simplicity, declaring
means "a map of some specific unknown type of keys and some specific unknown type of values".
But declaring
means "a set of entries of any type of key and value".
So that's where you run into trouble because the expression
m.entrySet()
doesn't want to return that but instead "a set of entries of some specific unknown type of keys and some specific unknown type of values". And those types are incompatible because generics aren't covariant: ASet<Type>
isn't aSet<SuperType>
.(See this fascinating post, which helps tease apart the nuances of nested wildcards: Multiple wildcards on a generic methods makes Java compiler (and me!) very confused.)
One workaround is to use a capture helper method, which takes advantage of the fact that formal type parameters can be nested:
That's a contrived example since
String
andInteger
are bothfinal
, but it shows the concept.A simpler workaround is the following:
This means adding non-
null
elements tos
isn't allowed, but in the case of theSet
returned byentrySet
, theadd
andaddAll
methods are unsupported anyway (thanks to newacct for clarifying this point).