Java Generics GetThis Trick Explanation

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 below multiple screens ...

 public abstract class Node <N extends Node<N>> { ... protected Node(N parent) { this.parent = parent; parent.children.add( (N)this ); // warning: unchecked cast } ... } 

Castes whose type is a type parameter cannot be checked at runtime and lead to an uncontrolled warning. This unsafe cast introduces potential for an unexpected ClassCastException and is best avoided.

Can someone give me an example where the above code throws a ClassCastException?

Thanks.

+7
java generics classcastexception
source share
1 answer

First code example

The first code example has a compilation error. You can test 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. List N can be a StupidNode list, where StupidNode is a subclass of Node. But the current instance may not be a StupidNode, it may be a different subclass of Node, so adding it may be wrong .


Second code example

Now the second code example is one where a developer who is annoyed by a compile-time error that he does not understand, believes that the compiler is wrong and is trying to force the cast. Such a listing does compilation of the code, but can break at runtime under the same conditions (as explained above).

Therefore, the compiler issues a warning to help you understand that something may be wrong.


Sample problem

For previous code samples, a problem may arise if the call code is written (for two subclasses of 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); 

In the second line, the code that will be run in the Node constructor will be interpreted as (replacing the N format parameter with the current NodeB parameter):

 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 the NodeA instance, while the Node constructor expects a NodeB ! Hence the error ...


UPDATE , as the comment asked: sample code for subclasses of NodeA (or NodeB).

 public class NodeA extends Node<NodeA> { public NodeA(NodeA parent) { super(parent); } } 
+5
source share

All Articles