2012-04-08 77 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