I am wondering what is wrong with this code:
Map extends String, ? extends Integer> m = null;
Set
This issue is addressed in this old Apache thread:
The problem is that the
entrySet()
method is returning aSet
, which is incompatible with the type> Set
. It's easier to describe why if I drop the> extends K
andextends V
part. So we haveSet
andSet
.> The first one,
Set
is a set of Map.Entries of different types - ie it is a heterogeneous collection. It could contain a> Map.Entry
and aMap.Entry
and any other pair of types, all in the same set.> On the other hand,
Set
is a homogenous collection of the same (albeit unknown) pair of types. Eg it might be a> Set
, so all of the entries in the set MUST be> Map.Entry
.
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
Map, ?> m;
means "a map of some specific unknown type of keys and some specific unknown type of values".
But declaring
Set> s;
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: A Set
isn't a Set
.
(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:
private void help(final Map map) {
final Set> entries = map.entrySet();
// logic
}
...
Map extends String, ? extends Integer> m = null;
help(m);
That's a contrived example since String
and Integer
are both final
, but it shows the concept.
A simpler workaround is the following:
Set extends Map.Entry extends String, ? extends Integer>> s = m.entrySet();
This means adding non-null
elements to s
isn't allowed, but in the case of the Set
returned by entrySet
, the add
and addAll
methods are unsupported anyway (thanks to newacct for clarifying this point).