Do not force the conversion of this class!
Unlike the terrible null pointer exception, which does not say anything about what is going to happen except to report null pointers, class cast exceptions are relatively easy to debug.
Class casts often occur in programs that are recursive downlink data structures, usually some parts of the current code are two levels down in each method call and are not scheduled to occur at the second downlink. Programmers can identify this problem by learning the Double descent error pattern.
Double Descent Error mode
This week's topic is Double descent error mode. It is indicated by a class-cast exception. It is caused by a recursive downlink composite data structure, which sometimes goes down multiple times in a recursive call. Doing so often requires adding type casts to compile the code. However, in this downward line, it is easy to forget to check that the appropriate invariants are met to ensure that these types of casts succeed.
Consider the following class hierarchy of int two-tree. Because we want to take into account the situation of the empty tree, we will not put the value field into the Leaf class. Since this decision makes all the leaves the same, we will retain a cell element with a static field of the leaf.
Listing 1. class hierarchy of int two-dollar Tree
abstract class Tree {
}
class Leaf extends Tree {
public static final Leaf ONLY = new Leaf();
}
class Branch extends Tree {
public int value;
public Tree left;
public Tree right;
public Branch(int _value, Tree _left, Tree _right) {
this.value = _value;
this.left = _left;
this.right = _right;
}
}
Now, suppose we want to add a method to the tree that determines whether any two consecutive nodes (such as a branch and one of its branches) contain a value of 0. We might add the following method (note: The last method will not be compiled in its current form):
Listing 2. Determine whether two consecutive nodes contain the value 0 method
// in class Tree:
public abstract boolean hasConsecutiveZeros();
// in class Leaf:
public boolean hasConsecutiveZeros() {
return false;
}
// in class Branch:
public boolean hasConsecutiveZeros() {
boolean foundOnLeft = false;
boolean foundOnRight = false;
if (this.value == 0) {
foundOnLeft = this.left.value == 0;
foundOnRight = this.right.value == 0;
}
if (foundOnLeft || foundOnRight) {
return true;
}
else {
foundOnLeft = this.left.hasConsecutiveZeros();
foundOnRight = this.right.hasConsecutiveZeros();
return foundOnLeft || foundOnRight;
}
}