1

I have an issue with a Tree we have implemented, here's a sample:

public interface TreeNode {
    TreeNode getParent();
    void setParent(TreeNode parent);

    List<TreeNode> getChildren();
    void setChildren(List<TreeNode> children);
}

So, this is easy enough until now, but we have some variations of the Tree, so that we have some interfaces like these:

public interface TreeNodeWithX extends TreeNode {
    String getX();
    void setX(String x);
}

public interface TreeNodeWithY extends TreeNode {
    Boolean getY();
    void setY(Boolean y);
}

So, I need that an object that is TreeNodeWithX (yes, an implementation of it) returns a TreeNodeWithX Object from its getParent method (same for the other methods from the TreeNode Interface).

Same behaviour from TreeNodeWithY, getParent() should return a TreeNodeWithY.

I have tried with some generics approaches, for instance this:

public interface TreeNode<T extends TreeNode> {
    T getParent();
    void setParent(T parent);

    List<T> getChildren();
    void setChildren(List<T> children);
}

However I always keep getting into trouble at some point in the implementation of the methods. My question is, am I going the right way with my generic interface or what am I doing wrong here?

The kind of recursive generic references are not really helping me out...

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Martin
  • 3,018
  • 1
  • 26
  • 45
  • What kind of trouble when implementing methods? – Thomas Jun 27 '13 at 14:32
  • 1
    Related: [Is there a way to refer to the current type with a type variable?](http://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current-type-with-a-type-variable) – Paul Bellora Jun 27 '13 at 14:34

3 Answers3

6

You want your node handle generic data, instead you're just generating generic TreeNodes to hold. IMO your interface should use T to handle the data and leave the other methods as they were:

public interface TreeNode<T> {
    TreeNode<T> getParent();
    void setParent(TreeNode<T> parent);

    List<TreeNode<T>> getChildren();
    void setChildren(List<TreeNode<T>> children);

    T getData();
    void setData(T data);
}

Now you can have TreeNode<String> and TreeNode<Boolean> which getData method will return a String or a Boolean (depending on the generic class argument passed).

TreeNode<String> treeNode = new SomeImplementationOfTreeNode<String>();
treeNode.setData("Hello world");
System.out.println(treeNode.getData()); // "Hello world"
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • That would work as long as it's predictable what you add, but in this case it's just two different implementations (or more) of the original tree, but what I'm looking for is not how to handle the type of the extra stuff, I'm looking for a way to be able to navigate the Tree and get return types (or input types) of the current implementation, not the upper parent class. – Martin Jun 27 '13 at 14:42
  • @Martin *That would work as long as it's predictable what you add* no, that's why it is generic, you can use `TreeNode` and the node will behave as a node that handles `AReallyOddClass`. Note that the navigation on the nodes should not be handled on the node instead on the `Tree` that handles the `TreeNode`s. – Luiggi Mendoza Jun 27 '13 at 14:46
  • OK, now I get what you mean... Will try it out and see if I run into other issues... – Martin Jun 27 '13 at 15:07
4

The type of TreeNode should be the type of the value held at each node, not a typed TreeNode (we already know we are dealing TreeNodes)

Try this:

/**
 * A Node in a Tree
 * @param <T> The type of the value held at each node
 */
public interface TreeNode<T> {
    TreeNode<T> getParent();
    void setParent(TreeNode<T> parent);

    List<TreeNode<T>> getChildren();
    void setChildren(List<TreeNode<T>> children);

    // A getter/setter of type `T` to the node for the "value" held there.
    T getValue();
    void setValue(T value);
}

Consider omitting the setter and making the field final in the implementation, passing the value into the constructor:

// Impl if TreeNode doesn't declare a setValue() method
public class MyTreeNode<T> implements TreeNode<T> {
    final T value;
    public MyTreeNode(T value) {
        this.value = value;
    }
    // rest of impl
}

Consider also making TreeNode a class if you have only one implementation in mind.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
2

TreeNode<T extends TreeNode> isn't the right approach. How about using a single interface:

public interface TreeNode<T> {
    TreeNode<T> getParent();
    void setParent(TreeNode<T> parent);

    List<TreeNode<T>> getChildren();
    void setChildren(List<TreeNode<T>> children);

    T getValue();
    void setValue(T x);
}
dbyrne
  • 59,111
  • 13
  • 86
  • 103