2013-07-15 57 views
2

我試圖擴展AbstractMap來創建一個MapTreeNode類(一個樹節點,其中子項通過鍵而不是索引來訪問)。實現Map.entrySet()的泛型有問題

我已經有越來越一套工作正常孩子的方法:

public class MapTreeNode<K,V> implements Map.Entry<K,V> { 
    private Map<K,MapTreeNode<K,V>> children = new HashMap<K,MapTreeNode<K,V>>(); 
    private Set<MapTreeNode<K,V>> child_set = null; 

    public Set<MapTreeNode<K,V>> children() { 
     if (child_set == null) 
      child_set = new ChildSet(); 

     return child_set; 
    } 

    ... 

    private final class ChildSet extends AbstractSet<MapTreeNode<K,V>> { 
     @Override 
     public Iterator<MapTreeNode<K,V>> iterator() { 
      return children.values().iterator(); 
     } 

     @Override 
     public int size() { 
      return MapTreeNode.this.childCount(); 
     } 
     ... 
    } 

} 

我想創建一個節點(Map<K,V>)的地圖視圖和重用child_set但我不相信這可能與Java的泛型:

public Map<K,V> asMap() { 
    return new AbstractMap<K,V>() { 
     @Override 
     public Set<Map.Entry<K,V>> entrySet() { 
      return child_set; // line 166 
     } 
    }; 
} 

這當然給

MapTreeNode:166: incompatible types 
found : java.util.Set<MapTreeNode<K,V>> 
required: java.util.Set<java.util.MapEntry<K,V>> 

有沒有一種方法可以重用我的ChildSet課程?

+1

相關:[是'名單''的名單'子類?爲什麼不是Java的泛型隱含多態?](http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p)。 –

回答

3

問題是與entrySet()的返回類型。它是Set<Map.Entry<K,V>>。正如你所知道的,Foo<A>Foo<B>對於不同的A和B無關,不管它們是如何相關的。

我認爲這是API中的一個設計錯誤。 entrySet()的退貨類型應該確實是Set<? extends Map.Entry<K,V>>。原因如下:如果您閱讀documentationentrySet(),它表示可以從Set中讀取事物,可以從Set中刪除事件(這會導致對底層映射的更改),但事情不能添加到Set中。這完全符合生產者的角色 - 你不需要添加任何東西。根據PECS規則,應使用extends-全球範圍內的卡片收集類型。

1

除非你需要特定的從MapTreeNode法的東西,把它當作一個Map.Entry,這意味着聲明child_set作爲

private Set<Map.Entry<K,V>> child_set = null; 

由於MapTreeNode延伸Map.Entry,你應該罰款。

+0

我試圖避免在「MapTreeNode」類的其餘部分將所有內容都轉換爲MapTreeNode,但也許這是唯一可行的選項。 –

+0

訣竅是,如果'MapTreeNode'僅用於替換Map.Entry的內部功能,那麼在將數據添加到Map時將條目轉換爲'MapTreeNode',並將'MapTreeNode'保留到外部世界。如果你需要一個全新的方法,只有'MapTreeNode'可以提供,那麼我們可以做其他的事情。 – JoshDM

+0

'MapTreeNode'是替代['DefaultMutableTreeNode'](http://docs.oracle.com/javase/6/docs/api/javax/swing/tree/DefaultMutableTreeNode.html)的替代品,所以有很多我修剪掉的其他功能以避免發佈代碼。 –

0

我的理解是你已經實現了新的類來獲得Entry類型的子類。爲了再次構建地圖,我將迭代一組條目並重新構建地圖。

不知道我是否像其他人一樣在這裏幫了忙,我無法完全閱讀這個課程包含的內容。

-1

你爲什麼不乾脆投這樣的:

public Map<K,V> asMap() { 
    return (Map<K,V>) this; 
} 
+0

「Map」中的某些方法並不能與樹節點的需求整齊排列,所以'MapTreeNode'不直接實現'Map'接口。此外,我也在其他幾個地方遇到過這個問題,所以我正在尋找解決此問題的通用方法。 –

+0

'MapTreeNode'沒有實現'Map' – newacct

1

以下是我已經能夠在避免重複代碼到目前爲止做的最好的:

private abstract class AbstractChildSet<T extends Map.Entry<K,V>> extends AbstractSet<T> { 
    @Override 
    public boolean remove(Object o) { 
     if (o == null || !(o instanceof Map.Entry)) { 
      return false; 
     } 

     MapTreeNode<K,V> node; 
     if (o instanceof MapTreeNode) 
      node = (MapTreeNode<K,V>) o; 
     else 
      node = MapTreeNode.this.child(((Map.Entry<K,V>) o).getKey()); 

     if (node == null || !isParentOf(node)) 
      return false; 

     node.removeFromParent(); 
     return true; 
    } 

    @Override 
    public int size() { 
     return MapTreeNode.this.childCount(); 
    } 

    @Override 
    public void clear() { 
     MapTreeNode.this.removeAllChildren(); 
    } 
} 

private final class ChildSet extends AbstractChildSet<MapTreeNode<K,V>> { 
    @Override  
    public boolean add(MapTreeNode<K,V> node) { 
     if (MapTreeNode.this.containsKey(node.getKey())) 
      return false; 

     MapTreeNode.this.addChild(node); 
     return true; 
    } 

    @Override 
    public Iterator<MapTreeNode<K,V>> iterator() { 
     return children.values().iterator(); 
    } 
} 

private final class EntrySet extends AbstractChildSet<Map.Entry<K,V>> { 
    @Override 
    public boolean add(Map.Entry<K,V> entry) { 
     if (MapTreeNode.this.containsKey(entry.getKey())) 
      return false; 

     MapTreeNode new_child = new HashMapTreeNode(MapTreeNode.this, entry.getKey(), entry.getValue()); 

     MapTreeNode.this.addChild(new_child); 
     return true; 
    } 

    @Override 
    public Iterator<Map.Entry<K,V>> iterator() { 
     return new EntryIterator(); 
    } 
}