【问题标题】:Generics specialization in javajava中的泛型专业化
【发布时间】:2012-04-21 10:01:06
【问题描述】:

谁能解释一下,为什么下一个代码没有编译?
我正在为它创建一个部分专业化的 Map 和 Map.Entry:

public class Trie<T> implements Map<String, T> {
    private class TrieEntry<S> implements Map.Entry<String, S> {
        // stupid implementation here
    }
    // uninterested code here
}

这里一切正常,但是我正在实现 entrySet() 方法:

public Set<java.util.Map.Entry<String, T>> entrySet() {
    Set<java.util.Map.Entry<String, T>> x = new HashSet<TrieEntry<T>>();
    // some uninterested code here
}

Eclipse 说

“类型不匹配:无法从HashSet&lt;Trie&lt;T&gt;.TrieEntry&lt;T&gt;&gt; 转换为Set&lt;Map.Entry&lt;String,T&gt;&gt;

所以,在我看来,TrieEntry&lt;T&gt; 应该展开为 Map.Entry&lt;String, T&gt;,它符合定义中的表达式。

我哪里错了?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    TrieEntry&lt;T&gt;Map.Entry&lt;String, T&gt; 是对的。 HashSet&lt;TrieEntry&lt;T&gt;&gt; 也是 Set&lt;TrieEntry&lt;T&gt;&gt;,但它不是 Set&lt;Map.Entry&lt;String, T&gt;&gt;

    如果是你可以这样做:

    Set<TrieEntry<T>> trieSet = ...;
    Set<Map.Entry<String, T>> mapSet = trieSet;
    mapSet.add(mapEntry);
    

    所以现在 trieSet 现在将包含一个Map.Entry&lt;String, T&gt;!这会破坏泛型。

    那么如何解决这个特殊问题呢?简单 - 使用有界通配符:

    Set<? extends Map.Entry<String, T>> x = new HashSet<TrieEntry<T>>();
    

    您可以将 ? extends Map.Entry&lt;String, T&gt; 解读为“至少是 Map.Entry&lt;String, T&gt; 的任何内容”。


    好的,现在解决您的实施问题。我实际上相信,由于方法 entrySet() 的当前定义,以这种方式无法解决。它应该返回? extends Map.Entry&lt;String, T&gt;,但它确实返回Map.Entry&lt;String, T&gt;

    实际上有一个bug report(或功能请求)就是针对这个问题的。查看提交日期和优先级,不能指望很快会修复。

    所以你有两个选择:

    1. 删除您的 TrieEntry 类并尝试改用 Map.Entry,将信息存储在您的键和/或值中。

    2. 删除Map接口,让你的自定义entrySet方法返回? extends Map.Entry&lt;String, T&gt;

    这两种解决方案可能都不理想,可能还有更好的解决方案,但目前我只能告诉你这些。

    【讨论】:

    • 是的,我对类型转换有点困惑。感谢您的澄清。
    • 为什么不能解决?仅创建 Set&lt;Map.Entry&lt;String, T&gt;&gt; x = new HashSet&lt;Map.Entry&lt;String, T&gt;&gt;(); 然后将 TrieEntry 对象放入其中有什么问题?
    • @newacct “不可解决”我的意思是,不可能让entrySet() 返回Set&lt;TrieEntry&lt;T&gt;&gt; - 当然你可以返回Set&lt;Map.Entry&lt;String, T&gt;&gt;,将TrieEntrys 放入它(见 Jespers 的回答!),但这不是类型安全的,这就是泛型的全部内容。例如,可以从Trie 继承并创建一个子类,其中entrySet() 不再返回TrieEntrys - 所以你需要类型检查和类型转换来保证类型安全。
    【解决方案2】:

    HashSet 当然实现了Set,但HashSet&lt;some subclass of X&gt; 不是Set&lt;X&gt; 的子类型。你可以这样做:

    Set<? extends Map.Entry<String, T>> x = new HashSet<TrieEntry<T>>();
    

    但我怀疑您稍后会在您的 entrySet 方法中遇到麻烦(您不能从该方法中返回它,因为它仍然需要您返回 Set&lt;Map.Entry&lt;String, T&gt;&gt;)。

    另一种解决方案是这样做:

    Set<Map.Entry<String, T>> x = new HashSet<Map.Entry<String, T>>();
    

    您可以将TrieEntry&lt;T&gt; 对象添加到此Set 并从entrySet() 返回。

    我认为您还可以通过省略类型参数 S 并仅使用封闭类中的 T 来简化您的 TrieEntry 内部类:

    class Trie<T> implements Map<String, T> {
        private class TrieEntry extends Map.Entry<String, T> {
            // ...
    
            @Override
            public T getValue() {
                // ...
            }
        }
    
        // ...
    }
    

    【讨论】:

    • 啊,这也是一个很好的解决方案!唯一的问题是传入的类型检查和强制转换,这很麻烦。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多