類別轉換異常(ClassCastException)較空指針異常易除錯,常發生於遞歸下行複合資料結構時。Double Descent(雙重下行) 錯誤模式描述:單次遞歸調用中下行兩層,第二層調度不當導致轉型失敗。
abstract class Tree { }
class Leaf extends Tree {
public static final Leaf ONLY = new Leaf();
}
class Branch extends Tree {
int value; Tree left, right;
Branch(int v, Tree l, Tree r) { value=v; left=l; right=r; }
}
需求:檢測任意兩連續節點是否皆為 0。Branch.hasConsecutiveZeros() 直接存取 left.value、right.value 導致編譯失敗(Tree 無 value 欄位)。強制轉型 ((Branch)left).value 雖通過編譯,但運行時若子節點為 Leaf 則拋出 ClassCastException。
// 錯誤實現
if (this.value == 0) {
foundOnLeft = ((Branch)this.left).value == 0; // 危險!
foundOnRight = ((Branch)this.right).value == 0;
}

左分支值為 0,嘗試將其子葉轉型為 Branch 失敗。
雖免除轉型,但將 ClassCastException 轉為更難診斷的 NullPointerException,降低代碼品質。
if (!(this.left instanceof Leaf)) { // 守衛不變量
foundOnLeft = ((Branch)this.left).value == 0;
}
缺點:新增 Tree 子類(如 LeafWithValue)需修改所有 instanceof 檢查。
在 Tree 定義 valueIs(int n),子類各自實現:
// Tree
abstract boolean valueIs(int n);
// Leaf
boolean valueIs(int n) { return false; }
// Branch
boolean valueIs(int n) { return value == n; }
// 使用
if (this.valueIs(0)) {
foundOnLeft = this.left.valueIs(0);
foundOnRight = this.right.valueIs(0);
}
優勢:封裝類型檢查邏輯,新增子類無需修改既有代碼,符合開放封閉原則,消除轉型與 instanceof 散佈。
將類型轉換視為斷言,不變量為其論證。優先透過多態方法分解轉型操作,而非散佈防禦性檢查。
下回將探討處理複雜輸入資料的錯誤模式。