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

时间:2013-05-25 20:25:43

标签: java generics

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

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();
        ...      
    }
    ... 
}

我收到编译时错误

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

接下来的解决方案可以完成这项工作,但实际上很难看:

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();
        ...      
    }
    ... 
 }

据我所知,问题是String中使用的类型变量Iterator<Map.Entry<String,String>>未扩展String <声明中使用的Map<? extends String, ? extends String> m <本身} / p>

4 个答案:

答案 0 :(得分:6)

没有Iterator

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

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<Entry<? extends String, ? extends String>>类型,它最初看起来是在iterator()上调用的Set<Entry<? extends String, ? extends String>>方法的返回类型,它反过来似乎是在[{1}}上调用entrySet()的返回类型。

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

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

有趣的是:

  

问题是Map<? extends String, ? extends String>方法正在返回a   entrySet(),   这与类型Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>>不兼容。   如果我放弃Set<Map.Entry<? extends K, ? extends V>>extends K部分,则更容易描述原因。   我们有extends VSet<Map.Entry<?, ?>

     

第一个,Set<Map.Entry<capture-of ?, capture-of ?>>是一组不同的Map.Entries   类型 - 即它是异构集合。它可能包含一个   Set<Map.Entry<?, ?>>Map.Entry<Long, Date>以及其他任何内容   一对类型,都在同一组中。

     

另一方面,Map.Entry<String, ResultSet>>是同质的   收集相同(虽然未知)的一对类型。例如它可能是一个   Set<Map.Entry<capture-of ?, capture-of ?>>,所以集合中的所有条目必须是   Set<Map.Entry<Long, Date>>

答案 1 :(得分:4)

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

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

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();
}

在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)->

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

我的理解是:如果您可以从String派生,可以说是LeftRightStringUpDownString,那么

  • Map<LeftRightString,LeftRightString>Map<? extends String, ? extends String>
  • 的子类型
  • Map<String, String>Map<? extends String, ? extends String>
  • 的子类型
  • Map<LeftRightString,LeftRightString>不是Map<String,String>
  • 的子类型

因此你的迭代器类型不匹配。如果它被允许那么,当它不起作用时,以下内容将起作用:

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

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