2012-04-08 42 views
2

有人可以解释为什么下一个代码不能编译?
我创建部分专业地图和Map.Entry的吧:java中的泛型专业化

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<Trie<T>.TrieEntry<T>>Set<Map.Entry<String,T>>

所以,在我的脑海TrieEntry<T>应展开为Map.Entry<String, T>,它符合定义中的表达式。

我在哪里错了?

回答

5

你是正确的,一个TrieEntry<T>Map.Entry<String, T>。 A HashSet<TrieEntry<T>>也是Set<TrieEntry<T>>,但它是而不是 a Set<Map.Entry<String, T>>

如果它是你可以这样做:

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

所以现在trieSet现在将包含Map.Entry<String, T>!这将打破泛型。

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

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

你可以阅读? extends Map.Entry<String, T>为“什么是至少Map.Entry<String, T>”。


好的,现在到您的实施问题。我实际上相信,由于方法entrySet()的当前定义,这是不可解决的这种方式。它应该返回? extends Map.Entry<String, T>,但它确实返回Map.Entry<String, T>

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

所以,你有两个选择:

  1. 放下你的类,并尝试使用Map.Entry,而不是在你的钥匙和/或值存储信息。

  2. 删除Map接口,并让您的自定义entrySet方法返回? extends Map.Entry<String, T>

这两种解决方案可能不理想,并且有可能是更好的解决方案,但是这是所有我目前可以告诉你。

+0

是的,我有点混淆类型铸造。感谢您的澄清。 – stborod 2012-04-08 21:35:07

+0

为什么不能解决?刚刚创建一个'Set > x = new HashSet >();'然后把'TrieEntry'对象放到它里面呢? – newacct 2012-04-08 23:21:09

+0

@newacct通过“not solvable”我的意思是,它不可能让'entrySet()'返回'Set >' - 当然你可以返回一个'Set >',把'TrieEntry (参见杰斯帕斯的回答!),但这不会是类型安全的,这就是泛泛而谈的一切。例如,可以从'Trie'继承并创建一个子类,其中'entrySet()'不再返回'TrieEntry' - 所以你需要typechecks和typecasts来保证类型安全。 – Anthales 2012-04-09 10:29:35

4

HashSet of course requires Set,但HashSet<some subclass of X>不是Set<X>的子类型。你可以这样做:

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

但我怀疑你会在你的entrySet方法陷入困境以后(你不能从方法返回它,因为它仍然需要你返回Set<Map.Entry<String, T>>)。

另一种解决方案是要做到这一点:

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

您可以添加TrieEntry<T>对象,这Set,也从entrySet()返回。

我想你也可以留出类型参数S简化内部类,只是使用T从封闭类:

class Trie<T> implements Map<String, T> { 
    private class TrieEntry extends Map.Entry<String, T> { 
     // ... 

     @Override 
     public T getValue() { 
      // ... 
     } 
    } 

    // ... 
} 
+0

嗯,这也是一个很好的解决方案!唯一的问题是传入的类型检查和投射,这是一个痛苦的屁股。 – Anthales 2012-04-08 21:24:19