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 Kandextends Vpart. 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