Upper bound wildcard variable iterator

Hi everyone, I'm trying to extend the HashMap<String,String> to apply the "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 following workflow 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 variable of type String used in Iterator<Map.Entry<String,String>> does not extend String (itself) used in the declaration Map<? extends String, ? extends String> m Map<? extends String, ? extends String> m

+7
source share
4 answers

Without iterator

The easiest way is to use a for-each loop. Even in this case, you need to parameterize the record with the same wildcards as on this map. The reason is that Entry<? extends String, ? extends String> Entry<? extends String, ? extends String> Entry<? extends String, ? extends String> not a subtype of Entry<String, String> . The fact that String is a final class does not matter here, because the compiler does not know about it.

 for (Entry<? extends String, ? extends String> entry : m.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); } 

With iterator

If you really need an Iterator, the syntax that compiles is a bit perplexing:

 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 initially expected that the Iterator<Entry<? extends String, ? extends String>> Iterator<Entry<? extends String, ? extends String>> Iterator<Entry<? extends String, ? extends String>> , which is first represented by the return type of the iterator() method called in Set<Entry<? extends String, ? extends String>> Set<Entry<? extends String, ? extends String>> Set<Entry<? extends String, ? extends String>> , which in turn seems to be the return type of entrySet() called Map<? extends String, ? extends String> Map<? extends String, ? extends String> Map<? extends String, ? extends String> .

However, this is a bit more complicated. I found a likely answer here:

http://mail-archives.apache.org/mod_mbox/harmony-dev/200605.mbox/% 3Cbb4674270605110156r4727e563of9ce24cdcb41a0c8@mail.gmail.com % 3E

The interesting part:

The problem is that the entrySet() method returns Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>> Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>> Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>> , which is incompatible with the type Set<Map.Entry<? extends K, ? extends V>> Set<Map.Entry<? extends K, ? extends V>> Set<Map.Entry<? extends K, ? extends V>> . It’s easier to describe why if I omit the extends K and extends V parts. So, we have Set<Map.Entry<?, ?> And Set<Map.Entry<capture-of ?, capture-of ?>> .

First, Set<Map.Entry<?, ?>> is a set of Map.Entries of different types - i.e. This is a diverse collection. It can contain Map.Entry<Long, Date> and a Map.Entry<String, ResultSet>> and any other pair of types, all in one set.

On the other hand, Set<Map.Entry<capture-of ?, capture-of ?>> is a homogeneous collection of the same (albeit unknown) pair of types. For example, it could be Set<Map.Entry<Long, Date>> , so all records in the set MUST be Map.Entry<Long, Date> .

+6
source

Wildcards are fuzzy, sometimes we want to turn wildcards into type variables that are more tangible.

The standard method represents a method with the corresponding type variables

 public void putAll(Map<? extends String, ? extends String> m) { _putAll(m); } <S1 extends String, S2 extends String> void _putAll(Map<S1, S2> m) { Iterator<Map.Entry<S1,S2>> iterator = m.entrySet().iterator(); } 

In java8 also try

 public void putAll(Map<? extends String, ? extends String> m) { m.forEach( (k,v)-> { ... }); } 

Types (k, v) are assumed to be captured types, like (S1, S2). However, it is also good if we correct their types as (String, String), due to the flexibility of the forEach signature

  m.forEach( (String k, String v)-> 
+4
source

Why not just avoid the iterator, as this code seems to be great for your putAll implementation:

 for(String s: m.keySet()){ put(s.toLowerCase(), m.get(s)); } 

As for why you cannot get around this error, I have no idea. I tried several options and nothing worked.

+1
source

My understanding is this: if it is possible that you could get from String , say classes called LeftRightString and UpDownString , then

  • Map<LeftRightString,LeftRightString> is a subtype of Map<? extends String, ? extends String> Map<? extends String, ? extends String>
  • Map<String, String> is a subtype of Map<? extends String, ? extends String> Map<? extends String, ? extends String>
  • but Map<LeftRightString,LeftRightString> not a subtype of Map<String,String>

Therefore, your type of iterator is incompatible. If this were allowed, the following would work when it should not work:

 void putAll(Map<? extends String, ? extends String> pm) { Map<String, String> m = pm; m.add(new UpDownString(), new UpDownString()); // ooops!! if ? was LeftRightString } 

(Update) I want to add that almost everything I said here in the Oracle Java tutorials is why I am puzzled by why so many people keep commenting that this is wrong. What is not in the tutorial can be found in the Java Specification . What I did not do is a workaround, but there are other answers.

0
source

All Articles