簡體   English   中英

泛型實現Map.entrySet()時遇到麻煩

[英]Generics trouble implementing Map.entrySet()

我正在嘗試擴展AbstractMap以創建MapTreeNode類(通過鍵而不是通過索引訪問子項的樹節點)。

我已經有一種方法可以讓一組孩子正常工作:

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_setchild_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類?

問題出在entrySet()的返回類型上。 它是Set<Map.Entry<K,V>> 如您所知,無論A和B如何關聯, Foo<A>Foo<B>都不兼容。

我認為這是API中的設計錯誤。 entrySet()的返回類型實際上應該是Set<? extends Map.Entry<K,V>> Set<? extends Map.Entry<K,V>> 這是為什么:如果您閱讀entrySet()文檔 ,它表示可以從Set中讀取內容,可以從Set中刪除內容(這會導致對基礎映射的更改),但是無法將內容添加到Set中。 。 這恰好適合生產者的角色-您無需在其中添加任何內容。 根據PECS規則,應使用extends通配符集合類型。

除非您需要MapTreeNode方法中的特定內容,否則將其視為Map.Entry ,這意味着將child_set聲明為

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

由於MapTreeNode擴展了Map.Entry ,所以應該沒問題。

到目前為止,這是我迄今為止為避免代碼重復所做的最大努力:

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();
    }
}

據我了解,您已經實施了新課程來接送Entry類型的孩子。 要再次構建地圖,我將遍歷一組條目並重新構建地圖。

不確定我是否像其他人一樣在這里提供了幫助,我無法完全閱讀該課程還包含的內容。

您為什么不像這樣簡單地投射它:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM