具有上限的通配符类型变量的迭代器

大家好,我尝试扩展HashMap来强制执行“全小写”规则

 public class HttpQueryMap extends HashMap { ... @Override public void putAll(Map m) { ... Iterator<Map.Entry> iterator = m.entrySet().iterator(); ... } ... } 

我收到编译时错误

 incompatible types required: Iterator<Entry> found: Iterator<Entry> 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 

下一个解决方案可以完成这项工作,但实际上很丑陋:

 public class HttpQueryMap extends HashMap { ... @Override public void putAll(Map m) { ... Map m_str=new HashMap(); m_str.putAll(m); Iterator<Map.Entry> iterator = m_str.entrySet().iterator(); ... } ... } 

据我所知,问题是Iterator<Map.Entry>中使用的类型变量String没有扩展Map m的声明中使用的String (本身) Map m Map m

没有迭代器

最简单的方法是使用for-each循环 。 即使在这种情况下,您也需要使用与给定映射中相同的通配符对Entry进行参数化。 原因是Entry Entry Entry不是Entry的子类型。 Stringfinal类的事实在这里是无关紧要的,因为编译器不知道这一点。

 for (Entry entry : m.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); } 

有了Iterator

如果你真的需要一个Iterator,那么编译的语法有点莫名其妙:

 Iterator> iterator = m.entrySet().iterator(); while (iterator.hasNext()) { Entry entry = iterator.next(); String key = entry.getKey(); String value = entry.getValue(); } 

我最初期望迭代器只是Iterator>类型Iterator> Iterator> Iterator> ,它最初看起来是在Set>上调用的iterator()方法的返回类型Set> Set> Set>反过来似乎是在Map上调用的entrySet()的返回类型Map Map Map

但是,它比这复杂一点。 我在这里找到了一个可能的答案:

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

有趣的是这个:

问题是entrySet()方法返回一个Set> Set> Set> ,这与Set>类型不兼容 Set> Set> 。 如果我放下extends Kextends V部分, extends V容易描述。 所以我们SetSet>

第一个, Set>是一组不同类型的Map.Entries – 即它是异构集合。 它可以包含Map.EntryMap.Entry>以及任何其他类型的对,它们都在同一个集合中。

另一方面, Set>是同一类(虽然未知)类型的同源集合。 例如,它可能是Set> ,因此集合中的所有条目必须是Map.Entry

通配符有点模糊,有时我们希望将通配符转换为更有形的类型变量。

标准方法是引入具有相应类型变量的方法

 public void putAll(Map m) { _putAll(m); }  void _putAll(Map m) { Iterator> iterator = m.entrySet().iterator(); } 

在java8中,也试试

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

(k,v)的类型被推断为捕获类型,就像(S1,S2)一样。 但是,如果我们将其类型修改为(String,String),由于forEach签名的灵活性,也可以

  m.forEach( (String k, String v)-> 

为什么不一起避免使用迭代器,因为这段代码似乎适用于putAll的实现:

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

至于为什么你似乎无法解决这个错误,我不知道。 我尝试了多种变体,似乎没有任何效果。

我的理解是这样的:如果你有可能从String派生,比如叫做LeftRightStringUpDownString类,那么

  • MapMap的子类型Map Map
  • MapMap的子类型Map Map
  • Map不是Map的子类型

因此,您的迭代器类型不匹配。 如果它被允许那么,当它不应该工作时,以下将工作:

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

(更新)我想补充一点,我在这里说的几乎所有内容都在Oracle Java教程中,所以我很困惑为什么这么多人一直在评论这是错误的。 可以在Java规范中找到教程中没有的内容。 我没有做的是给出一个解决方法,但其他答案有。