8

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

4

4 回答 4

7

没有迭代器

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

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

使用迭代器

如果你真的需要一个迭代器,编译的语法有点莫名其妙:

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

我最初期望迭代器只有 type Iterator<Entry<? extends String, ? extends String>>,起初它似乎是调用 a 的iterator()方法的返回类型,而后者又似乎是调用 onSet<Entry<? extends String, ? extends String>>的返回类型。entrySet()Map<? extends String, ? extends String>

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

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

有趣的部分是:

问题是该entrySet()方法返回一个 Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>>与类型不兼容的Set<Map.Entry<? extends K, ? extends V>>。更容易描述为什么如果我放弃extends Kandextends V部分。所以我们有Set<Map.Entry<?, ?>Set<Map.Entry<capture-of ?, capture-of ?>>

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

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

于 2013-05-25T21:08:31.367 回答
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)->
于 2013-05-26T00:25:27.417 回答
1

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

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

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

于 2013-05-25T21:22:26.457 回答
0

我的理解是这样的:如果你可以派生自String,比如说叫做LeftRightStringand的类UpDownString,那么

  • 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 规范中找到。我没有做的是给出一个解决方法,但其他答案有。

于 2013-05-25T21:47:59.870 回答