Is it a design flaw to have a static final variable in a generic class? Consider the class below, all references to Node.SOIL give rise to warnings. What is a good way of going about solving this problem?
public class Node<E> {
private static int nodeCounter = 0;
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final Node SOIL = new Node(null, null); // <-- HERE
public static void resetSOIL(){
SOIL.children = null; // <-- HERE
}
private Node<E> parent;
private Set<Node<E>> children;
protected Set<Node<E>> isomorphs;
private E data;
private int id;
public Node(Node<E> parent, E data){
this.parent = parent;
this.data = data;
this.id = ++nodeCounter;
}
public boolean isRoot(){
return (this.getParent() == SOIL);
}
// utility methods
....
}
You’ve defined a type
Node<E>which represents a node in a tree of E’s. For example,Node<Integer>is a node in a tree of Integers, andNode<String>is a node in a tree of Strings.Now you want a variable
SOILthat contains all the roots of these various trees of different types (hehe, soil, I get it now). Set aside the static field issue for now. What should the class ofSOILbe? In other words, how do we declare and create theSOILinstance?Since
SOILis going to have children that areNode<Integer>andNode<String>then it has to be something likeNode<?>orNode<Object>. You can’t instantiate an object using a wildcard type argument, so the best you can do is something like this:(If you use the Java 7 diamond construct
new Node<>(...)it ends up usingObjectin this case anyway.)The problem is, this still doesn’t work. The way the
Node<E>type is defined is as a homogeneous tree of E’s. But one subtree ofSOILis a tree of Integers and another subtree is a tree of Strings, soSOILwants to be a heterogeneous tree. Therefore,SOILcannot be aNode<E>for anyE.At least, not in a type-safe fashion. You could write the code to do this, but you’d have to add some casts, and you’d get unchecked warnings. These warnings would tell you that this approach isn’t working. For example, consider some code that takes a
Node<E>and operates on all of its siblings (that is, the other children of its parent). Some code that took aNode<Integer>could call this and end up with a bunch of instances ofNode<String>and start throwingClassCastExceptions.You might consider making
SOILaSet<Node<?>>and not making it be the parent of all the roots. You could make it a static field somewhere, but make it private.