在泛型类中使用静态最终变量是一个设计缺陷吗?考虑下面的类,所有对Node.SOIL
的引用都会引发警告。什么是解决这个问题的好方法?
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
....
}
答案 0 :(得分:2)
您已经定义了一个类型Node<E>
,它代表了E树中的一个节点。例如,Node<Integer>
是整数树中的节点,Node<String>
是字符串树中的节点。
现在你想要一个变量SOIL
,它包含不同类型的这些不同树的所有根(呵呵,土壤,我现在得到它)。暂时搁置静态字段问题。 SOIL
的班级应该是什么?换句话说,我们如何声明和创建SOIL
实例?
Node</*something1*/> SOIL = new Node</*something2*/>(null, null);
由于SOIL
将生成Node<Integer>
和Node<String>
的子项,因此它必须类似于Node<?>
或Node<Object>
。您不能使用通配符类型参数来实例化对象,因此您可以做的最好是:
Node<?> SOIL = new Node<Object>(null, null);
(如果您使用Java 7钻石构造new Node<>(...)
,在这种情况下最终会使用Object
。)
问题是,这仍然不起作用。定义Node<E>
类型的方式是E的同类树。但是SOIL
的一个子树是整数树,而另一个子树是字符串树,因此SOIL
想要成为异构树。因此,对于任何SOIL
,Node<E>
都不能是E
。
至少,不是以类型安全的方式。您可以编写代码来执行此操作,但您必须添加一些强制转换,并且您将获得未经检查的警告。这些警告会告诉您这种方法不起作用。例如,考虑一些采用Node<E>
并对其所有兄弟节点(即其父节点的其他子节点)进行操作的代码。一些使用Node<Integer>
的代码可以调用此代码并最终得到一堆Node<String>
的实例并开始抛出ClassCastExceptions
。
您可以考虑将SOIL
设为Set<Node<?>>
,而不是让它成为所有词根的父级。你可以把它变成某个地方的静态字段,但把它变成私有的。
答案 1 :(得分:1)
拥有静态最终变量不是设计缺陷。出现警告是因为您在未提供类型参数的情况下声明泛型类型的实例:
public static final Node SOIL = new Node(null, null); // <-- HERE
当编译器期望类似的东西时:
public static final Node<SomeType> SOIL = new Node<SomeType>(null, null);
答案 2 :(得分:0)
您可以使用它来避免抑制警告并且效果很好:
private static final Node<Object> SOIL = new Node<Object>(null, null);
回答你的另一个问题:
Is it a design flaw to have a static final variable in a generic class?
没有,通常使用它作为Node类型的null变量的默认值,即有效Java编程中的NULL对象