我正在阅读有关Java泛型的文章,遇到了这个话题,我对此感到有些困惑。
来自:http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205
public abstract class Node <N extends Node<N>> {
private final List<N> children = new ArrayList<N>();
private final N parent;
protected Node(N parent) {
this.parent = parent;
parent.children.add(this); // error: incompatible types
}
public N getParent() {
return parent;
}
public List<N> getChildren() {
return children;
}
}
public class SpecialNode extends Node<SpecialNode> {
public SpecialNode(SpecialNode parent) {
super(parent);
}
}
向下滚动几个屏幕...
public abstract class Node <N extends Node<N>> {
...
protected Node(N parent) {
this.parent = parent;
parent.children.add( (N)this ); // warning: unchecked cast
}
...
}
有人可以举一个上面的代码抛出ClassCastException的例子吗?
谢谢。
最佳答案
第一个代码样本
在第一个代码示例中,有一个编译错误。您可以在IDE中自己进行验证。
我的说:The method add(N) in the type List<N> is not applicable for the arguments (Node<N>)
问题在于N是Node的子类型。 N的列表可能是StupidNode的列表,其中StupidNode是Node的子类。但是当前实例可能不是StupidNode,它可能是Node的另一个子类,因此添加它可能是错误的。
第二个代码样本
现在,第二个代码示例是一个示例,在该示例中,开发人员由于不理解他的编译时错误而感到恼火,他认为编译器是错误的,并试图强制进行强制转换。
这样的转换可以编译代码,但是可以在相同的条件下(如上所述)在运行时中断。
因此,编译器会发出警告,以帮助您理解某些错误。
样本问题
对于前面的两个代码示例,如果调用代码写入(对于NodeA
的两个子类NodeB
和Node
),则可能会发生此问题:
Node<NodeA> root = new NodeA<NodeA>(null);
// code needs a change, to be able to handle the root, that has no parent
// The line with parent.children will crash with a NullPointerException
Node<NodeB> child = new NodeB<NodeB>(root);
在第二行中,将在Node
的构造函数中运行的代码将解释为(将格式参数N
替换为当前参数NodeB
):public abstract class Node <NodeB> {
private final List<NodeB> children = new ArrayList<NodeB>();
private final NodeB parent;
protected Node(NodeB parent) {
this.parent = parent;
parent.children.add(this); // error: incompatible types
}
// ...
}
如您所见,调用者的第二行将传递一个NodeA
实例,而Node
的构造函数则期望一个NodeB
!因此,错误...根据注释要求更新:子类NodeA(或NodeB)的示例代码。
public class NodeA extends Node<NodeA> {
public NodeA(NodeA parent) {
super(parent);
}
}