I am reading about Java Generics and I came across this topic where I am a bit confused.
From : 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);
}
}
Scrolling lower a couple of screens...
public abstract class Node <N extends Node<N>> {
...
protected Node(N parent) {
this.parent = parent;
parent.children.add( (N)this ); // warning: unchecked cast
}
...
}
Casts whose target type is a type parameter cannot be verified at runtime and lead to an unchecked warning. This unsafe cast introduces the potential for unexpected ClassCastException s and is best avoided.
Could someone give me an example where the above code throws a ClassCastException ?
Thanks.
In the first code sample, there is a compile-error. You can verify it yourself in your IDE.
Mine says : The method add(N) in the type List<N> is not applicable for the arguments (Node<N>)
The problem is that N is a subtype of Node. The List of N might be a list of StupidNode, where StupidNode is a subclass of Node. But the current instance might not be a StupidNode, it could be a different subclass of Node, so adding it could be wrong.
Now the second code sample is one where the developer, annoyed by the compile-time error that he doesn't understand, believes the compiler is wrong and try to force a cast. Such a cast make the code compile, but could break at runtime in the same conditions (as explained higher).
Therefore, the compiler issues a warning, to help you understand that something could be wrong.
For both previous code samples, the problem could happen if the calling code writes (for two subclasses NodeA
and NodeB
of 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);
On the second line, the code that will run in the constructor of Node
will interpret like (replacing the format parameter N
by the current parameter 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
}
// ...
}
As you can see, the second line of the caller will pass a NodeA
instance, while the constructor of Node
expects a NodeB
! Hence the error...
UPDATE as asked by comment : sample code for subclasses NodeA (or NodeB).
public class NodeA extends Node<NodeA> {
public NodeA(NodeA parent) {
super(parent);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With