I'm trying to extend AbstractMap
to create a MapTreeNode
class (a tree node where children are accessed by key rather than by index).
I already have a method for getting a set of the children which works fine:
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();
}
...
}
}
I'd like to create a map view of a node (Map<K,V>
) and reuse child_set
but I'm not sure it's possible with Java's generics:
public Map<K,V> asMap() {
return new AbstractMap<K,V>() {
@Override
public Set<Map.Entry<K,V>> entrySet() {
return child_set; // line 166
}
};
}
this of course gives
MapTreeNode:166: incompatible types
found : java.util.Set<MapTreeNode<K,V>>
required: java.util.Set<java.util.MapEntry<K,V>>
Is there a way I can reuse my ChildSet
class for this?
The problem is with entrySet()
's return type. It is Set<Map.Entry<K,V>>
. And as you know, Foo<A>
is not compatible with Foo<B>
for different A and B no matter how they're related.
I would argue that this was a design mistake in the API. The return type of entrySet()
should really be Set<? extends Map.Entry<K,V>>
. Here is why: If you read the documentation for entrySet()
, it says that things can be read from the Set, things can be removed from the Set (which causes changes to the underlying map), but things cannot be added to the Set. This exactly fits the role of a Producer -- you do not add things to it. Per the PECS rule, an extends
-wildcard collection type should be used.