具有上限的通配符类型变量的迭代器
大家好,我尝试扩展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 extends String, ? extends String>
Entry extends String, ? extends String>
Entry extends String, ? extends String>
不是Entry
的子类型。 String
是final
类的事实在这里是无关紧要的,因为编译器不知道这一点。
for (Entry extends String, ? extends String> entry : m.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); }
有了Iterator
如果你真的需要一个Iterator,那么编译的语法有点莫名其妙:
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(); }
我最初期望迭代器只是Iterator
类型Iterator
Iterator
Iterator
,它最初看起来是在Set
上调用的iterator()
方法的返回类型Set
Set
Set
反过来似乎是在Map extends String, ? extends String>
上调用的entrySet()
的返回类型Map extends String, ? extends String>
Map extends String, ? extends String>
Map extends String, ? extends String>
。
但是,它比这复杂一点。 我在这里找到了一个可能的答案:
有趣的是这个:
问题是
entrySet()
方法返回一个Set
> Set
> Set
,这与> Set
类型不兼容> Set
> Set
。 如果我放下> extends K
并extends V
部分,extends V
容易描述。 所以我们Set
和Set
。> 第一个,
Set
是一组不同类型的Map.Entries – 即它是异构集合。 它可以包含> Map.Entry
和Map.Entry
以及任何其他类型的对,它们都在同一个集合中。> 另一方面,
Set
是同一类(虽然未知)类型的同源集合。 例如,它可能是> Set
,因此集合中的所有条目必须是> Map.Entry
。
通配符有点模糊,有时我们希望将通配符转换为更有形的类型变量。
标准方法是引入具有相应类型变量的方法
public void putAll(Map extends String, ? extends String> m) { _putAll(m); } void _putAll(Map m) { Iterator> iterator = m.entrySet().iterator(); }
在java8中,也试试
public void putAll(Map extends String, ? extends String> 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
派生,比如叫做LeftRightString
和UpDownString
类,那么
-
Map
是Map extends String, ? extends String>
的子类型Map extends String, ? extends String>
Map extends String, ? extends String>
-
Map
是Map extends String, ? extends String>
的子类型Map extends String, ? extends String>
Map extends String, ? extends String>
- 但
Map
不是Map
的子类型
因此,您的迭代器类型不匹配。 如果它被允许那么,当它不应该工作时,以下将工作:
void putAll(Map extends String, ? extends String> pm) { Map m = pm; m.add(new UpDownString(), new UpDownString()); // ooops!! if ? was LeftRightString }
(更新)我想补充一点,我在这里说的几乎所有内容都在Oracle Java教程中,所以我很困惑为什么这么多人一直在评论这是错误的。 可以在Java规范中找到教程中没有的内容。 我没有做的是给出一个解决方法,但其他答案有。