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 isSet<Map.Entry<K,V>>
. And as you know,Foo<A>
is not compatible withFoo<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 beSet<? extends Map.Entry<K,V>>
. Here is why: If you read the documentation forentrySet()
, 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, anextends
-wildcard collection type should be used.