简体   繁体   English

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

[英]Iterator of a wildcard type variable with upper bound

Hello everybody I try to extend a HashMap<String,String> to enforce a "all-lowercase" rule 大家好,我尝试扩展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();
        ...      
    }
    ... 
}

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

Without Iterator 没有迭代器

The easiest way is to use a for-each loop . 最简单的方法是使用for-each循环 Even in this case, you need the parametrize the Entry with the same wildcards as in the given map. 即使在这种情况下,您也需要使用与给定映射中相同的通配符对Entry进行参数化。 The reason is that Entry<? extends String, ? extends String> 原因是Entry<? extends String, ? extends String> Entry<? extends String, ? extends String> Entry<? extends String, ? extends String> is not a subtype of Entry<String, String> . Entry<? extends String, ? extends String>不是Entry<String, String>的子类型。 The fact that String is a final class is irrelevant here, because the compiler has no knowledge of that. Stringfinal类的事实在这里是无关紧要的,因为编译器不知道这一点。

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

With Iterator 有了Iterator

If you really need an Iterator, the syntax that does compile is a bit baffling: 如果你真的需要一个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();
}

I originally expected the iterator to be only of type Iterator<Entry<? extends String, ? extends String>> 我最初期望迭代器只是Iterator<Entry<? extends String, ? extends String>>类型Iterator<Entry<? extends String, ? extends String>> Iterator<Entry<? extends String, ? extends String>> Iterator<Entry<? extends String, ? extends String>> , which at first appears to be the return type of iterator() method called on a Set<Entry<? extends String, ? extends String>> Iterator<Entry<? extends String, ? extends String>> ,它最初看起来是在Set<Entry<? extends String, ? extends String>>上调用的iterator()方法的返回类型Set<Entry<? extends String, ? extends String>> Set<Entry<? extends String, ? extends String>> Set<Entry<? extends String, ? extends String>> which in turns appears to be the return type of entrySet() called on Map<? extends String, ? extends String> Set<Entry<? extends String, ? extends String>>反过来看起来是在Map<? extends String, ? extends String>上调用的entrySet()的返回类型Map<? extends String, ? extends String> Map<? extends String, ? extends String> Map<? extends String, ? extends String> . Map<? extends String, ? extends String>

However, it is a bit more complex than that. 但是,它比这复杂一点。 I've found a probable answer in here: 我在这里找到了一个可能的答案:

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

The interesting part is this: 有趣的是这个:

The problem is that the entrySet() method is returning a Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>> 问题是entrySet()方法返回一个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<capture-of ? extends K, capture-of ? extends V>> ,这与Set<Map.Entry<? extends K, ? extends V>>类型不兼容 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 drop the extends K and extends V part. 如果我放下extends Kextends V部分, extends V容易描述。 So we have Set<Map.Entry<?, ?> and Set<Map.Entry<capture-of ?, capture-of ?>> . 所以我们Set<Map.Entry<?, ?>Set<Map.Entry<capture-of ?, capture-of ?>>

The first one, Set<Map.Entry<?, ?>> is a set of Map.Entries of different types - ie it is a heterogeneous collection. 第一个, Set<Map.Entry<?, ?>>是一组不同类型的Map.Entries - 即它是异构集合。 It could contain a Map.Entry<Long, Date> and a Map.Entry<String, ResultSet>> and any other pair of types, all in the same set. 它可以包含Map.Entry<Long, Date>Map.Entry<String, ResultSet>>以及任何其他类型的对,它们都在同一个集合中。

On the other hand, Set<Map.Entry<capture-of ?, capture-of ?>> is a homogenous collection of the same (albeit unknown) pair of types. 另一方面, Set<Map.Entry<capture-of ?, capture-of ?>>是同一类(虽然未知)类型的同源集合。 Eg it might be a Set<Map.Entry<Long, Date>> , so all of the entries in the set MUST be Map.Entry<Long, Date> . 例如,它可能是Set<Map.Entry<Long, Date>> ,因此集合中的所有条目必须是Map.Entry<Long, Date>

Wildcards are kind of vague, sometimes we want to turn wildcards into type variables which are more tangible. 通配符有点模糊,有时我们希望将通配符转换为更有形的类型变量。

The standard way is introducing a method with 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 在java8中,也试试

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

The types of (k,v) are inferred to be captured types, just like (S1,S2). (k,v)的类型被推断为捕获类型,就像(S1,S2)一样。 However, it is also OK if we fix their types as (String,String), due to the flexibility of the signature of forEach 但是,如果我们将其类型修改为(String,String),由于forEach签名的灵活性,也可以

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

Why not just avoid the iterator all together as this code seems to work just fine for your implementation of putAll : 为什么不一起避免使用迭代器,因为这段代码似乎适用于putAll的实现:

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

As to why you can't seem to work around that error, I have no idea. 至于为什么你似乎无法解决这个错误,我不知道。 I tried multiple variants and nothing seemed to work. 我尝试了多种变体,似乎没有任何效果。

My understanding is this: If it were possible that you could derive from String , say classes called LeftRightString and UpDownString , then 我的理解是这样的:如果你可以从String派生,比如称为LeftRightStringUpDownString类,那么

  • Map<LeftRightString,LeftRightString> is a subtype of Map<? extends String, ? extends String> Map<LeftRightString,LeftRightString>Map<? extends String, ? extends String>的子类型Map<? extends String, ? extends String> Map<? extends String, ? extends String>
  • Map<String, String> is a subtype of Map<? extends String, ? extends String> Map<String, String>Map<? extends String, ? extends String>的子类型Map<? extends String, ? extends String> Map<? extends String, ? extends String>
  • but Map<LeftRightString,LeftRightString> is not a subtype of Map<String,String> Map<LeftRightString,LeftRightString>不是Map<String,String>的子类型

Therefore your iterator type mismatches. 因此,您的迭代器类型不匹配。 If it were allowed then, 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 is in the Oracle Java tutorials so I'm baffled at why so many people keep commenting that this is wrong. (更新)我想补充一点,我在这里说的几乎所有内容都在Oracle Java教程中,所以我很困惑为什么这么多人一直在评论这是错误的。 And what is not in the tutorial can be found in the Java Specification . 可以在Java规范中找到教程中没有的内容。 What I haven't done is give a workaround but other answers have. 我没有做的是给出一个解决方法,但其他答案有。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM