【问题标题】:Bounded-wildcard related compiler error有界通配符相关的编译器错误
【发布时间】:2023-03-06 17:06:01
【问题描述】:

我想知道这段代码有什么问题:

Map <? extends String, ? extends Integer> m = null;
Set<Map.Entry<? extends String, ? extends Integer>> s = m.entrySet();

编译器报错:

类型不匹配:无法从 Set&lt;Map.Entry&lt;capture#1-of ? extends String,capture#2-of ? extends Integer&gt;&gt; 转换为 Set&lt;Map.Entry&lt;? extends String,? extends Integer&gt;&gt;

s 的类型应该是什么? Eclipse 建议使用Set&lt;?&gt;,但我试图更具体。

【问题讨论】:

    标签: java generics bounded-wildcard


    【解决方案1】:

    this old Apache thread 解决了这个问题:

    问题是entrySet() 方法返回一个 Set&lt;Map.Entry&lt;capture-of ? extends K, capture-of ? extends V&gt;&gt;, 这与Set&lt;Map.Entry&lt;? extends K, ? extends V&gt;&gt; 类型不兼容。 更容易描述为什么如果我删除 extends Kextends V 部分。 所以我们有Set&lt;Map.Entry&lt;?, ?&gt;Set&lt;Map.Entry&lt;capture-of ?, capture-of ?&gt;&gt;

    第一个,Set&lt;Map.Entry&lt;?, ?&gt;&gt;是一组不同的Map.Entries 类型 - 即它是一个异构集合。它可能包含一个 Map.Entry&lt;Long, Date&gt;Map.Entry&lt;String, ResultSet&gt;&gt; 和任何其他 一对类型,都在同一个集合中。

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

    问题的症结在于顶级通配符capture,这意味着它们本质上是一次性类型参数。相比之下,嵌套通配符不捕获,并且具有某种不同的含义。

    所以,为了简单起见,去掉边界,声明

    Map<?, ?> m;
    

    表示“一些特定未知类型的键和一些特定未知类型的值的映射”。

    但是声明

    Set<Map.Entry<?, ?>> s;
    

    表示“任何类型的键和值的一组条目”。

    这就是你遇到麻烦的地方,因为表达式 m.entrySet() 不想返回它,而是“一组某些特定未知类型的键和一些特定的条目 未知类型的值”。这些类型不兼容,因为泛型 aren't covariant: A Set&lt;Type&gt; 不是 Set&lt;SuperType&gt;

    (请参阅这篇引人入胜的帖子,它有助于梳理嵌套通配符的细微差别:Multiple wildcards on a generic methods makes Java compiler (and me!) very confused。)

    一种解决方法是使用capture helper 方法,它利用了可以嵌套形式类型参数的事实:

    private <K extends String, V extends Integer> void help(final Map<K, V> map) {
        final Set<Map.Entry<K, V>> entries = map.entrySet();
        // logic
    }
    
    ...
    
    Map<? extends String, ? extends Integer> m = null;
    help(m);
    

    这是一个人为的例子,因为 StringInteger 都是 final,但它显示了这个概念。

    一个更简单的解决方法如下:

    Set<? extends Map.Entry<? extends String, ? extends Integer>> s = m.entrySet();
    

    这意味着不允许将非null 元素添加到s,但是对于entrySet 返回的Set,无论如何都不支持addaddAll 方法(谢谢clarifying this point 的 newacct)。

    【讨论】:

    • “这实际上使 s 成为只读的,但从上下文来看这不应该是一个问题。”从技术上讲,它只会使它无法添加(非null)东西;仍然允许删除。这保证不是问题——entrySet() 的文档说返回的Set“不支持 add 或 addAll 操作。”
    • @newacct 你说得对,“只读”是一个有问题的描述 - 已修复。
    • 是的。我也认为Java API中entrySet()方法的签名是错误的。 Set&lt;? extends Map.Entry&lt;...&gt;&gt; 就足够了,它使 Map 的实现更加灵活,例如返回一个由自定义条目子类或其他东西参数化的集合。
    • 另一种解决方案是显式使用不可修改的集合:Set&lt;Map.Entry&lt;?, ?&gt;&gt; set = Collections.&lt;Map.Entry&lt;?, ?&gt;&gt;unmodifiableSet(entries.entrySet())
    猜你喜欢
    • 1970-01-01
    • 2020-12-13
    • 1970-01-01
    • 2012-08-08
    • 2013-11-07
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    • 2014-12-23
    相关资源
    最近更新 更多