我正在尝试扩展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_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
课程呢?
答案 0 :(得分:3)
问题在于entrySet()
的返回类型。它是Set<Map.Entry<K,V>>
。如您所知,Foo<A>
对于不同的A和B与Foo<B>
不兼容,无论它们如何相关。
我认为这是API中的设计错误。 entrySet()
的返回类型应该是Set<? extends Map.Entry<K,V>>
。原因如下:如果您阅读entrySet()
的{{3}},它表示可以从Set中读取内容,可以从Set中删除内容(这会导致更改底层地图),但是无法添加到Set中。这完全符合制作人的角色 - 你不会添加东西。根据PECS规则,应使用extends
- 通配符集合类型。
答案 1 :(得分:1)
除非您需要MapTreeNode
方法中的特定内容,否则请将其视为Map.Entry
,即将child_set
声明为
private Set<Map.Entry<K,V>> child_set = null;
由于MapTreeNode
延伸Map.Entry
,您应该没问题。
答案 2 :(得分: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();
}
}
答案 3 :(得分:0)
我明白你已经实现了新的类来获得类型为Entry的子项。为了再次构建一个地图,我将遍历一组条目并重建地图。
我不确定我是否像其他人一样帮助我,我无法完全阅读此课程中包含的内容。
答案 4 :(得分:-1)
你为什么不这样简单地投下它:
public Map<K,V> asMap() {
return (Map<K,V>) this;
}