Hello everybody I try to extend a HashMap<String,String>
to enforce a "all-lowercase" rule
public class HttpQueryMap extends HashMap<String,String>
{
...
@Override
public void putAll(Map<? extends String, ? extends String> m)
{
...
Iterator<Map.Entry<String,String>> iterator = m.entrySet().iterator();
...
}
...
}
I get a compile-time error
incompatible types
required: Iterator<Entry<String,String>>
found: Iterator<Entry<CAP#1,CAP#2>>
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends String from capture of ? extends String
CAP#2 extends String from capture of ? extends String
The next work-around does the job but it is really ugly:
public class HttpQueryMap extends HashMap<String,String>
{
...
@Override
public void putAll(Map<? extends String, ? extends String> m)
{
...
Map<String,String> m_str=new HashMap<String,String>();
m_str.putAll(m);
Iterator<Map.Entry<String,String>> iterator = m_str.entrySet().iterator();
...
}
...
}
As far as I understand the problem is that the type variable String
used in the Iterator<Map.Entry<String,String>>
does not extend String
(itself) used in the declaration of Map<? extends String, ? extends String> m
The easiest way is to use a for-each loop. Even in this case, you need the parametrize the Entry with the same wildcards as in the given map. The reason is that Entry<? extends String, ? extends String>
is not a subtype of Entry<String, String>
. The fact that String
is a final
class is irrelevant here, because the compiler has no knowledge of that.
for (Entry<? extends String, ? extends String> entry : m.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
If you really need an Iterator, the syntax that does compile is a bit baffling:
Iterator<? extends Entry<? extends String, ? extends String>> iterator =
m.entrySet().iterator();
while (iterator.hasNext()) {
Entry<? extends String, ? extends String> entry = iterator.next();
String key = entry.getKey();
String value = entry.getValue();
}
I originally expected the iterator to be only of type Iterator<Entry<? extends String, ? extends String>>
, which at first appears to be the return type of iterator()
method called on a Set<Entry<? extends String, ? extends String>>
which in turns appears to be the return type of entrySet()
called on Map<? extends String, ? extends String>
.
However, it is a bit more complex than that. I've found a probable answer in here:
http://mail-archives.apache.org/mod_mbox/harmony-dev/200605.mbox/%[email protected]%3E
The interesting part is this:
The problem is that the
entrySet()
method is returning aSet<Map.Entry<capture-of ? extends K, capture-of ? extends V>>
, which is incompatible with the typeSet<Map.Entry<? extends K, ? extends V>>
. It's easier to describe why if I drop theextends K
andextends V
part. So we haveSet<Map.Entry<?, ?>
andSet<Map.Entry<capture-of ?, capture-of ?>>
.The first one,
Set<Map.Entry<?, ?>>
is a set of Map.Entries of different types - ie it is a heterogeneous collection. It could contain aMap.Entry<Long, Date>
and aMap.Entry<String, ResultSet>>
and any other pair of types, all in the same set.On the other hand,
Set<Map.Entry<capture-of ?, capture-of ?>>
is a homogenous collection of the same (albeit unknown) pair of types. Eg it might be aSet<Map.Entry<Long, Date>>
, so all of the entries in the set MUST beMap.Entry<Long, Date>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With