靜態類型系統是 Java 核心優勢,能在編譯期消除類型錯誤。但許多程式放棄此保護,改用欄位標記區分資料類型,導致 Impostor Type(偽裝類型) 錯誤:標記對資料誤貼標籤,引發邏輯錯誤。
public class Form {
String shape; // 偽裝類型標記
double scale;
Form(String _shape, double _scale) { shape=_shape; scale=_scale; }
double getArea() {
if (shape.equals("square")) return scale * scale;
else if (shape.equals("circle")) return Math.PI * scale * scale;
else return scale * (scale * Math.sqrt(3) / 4); // 三角形
}
}
new Form("sqaure", 2) 編譯通過,運行時當作三角形計算面積,無錯誤訊息,結果似合理實錯誤。getArea() 來源代碼,違反開放封閉原則。String 欄位可意外/惡意修改,導致物件行為突變。
abstract class Form {
double scale;
Form(double _scale) { scale = _scale; }
abstract double getArea();
}
class Square extends Form {
Square(double s) { super(s); }
double getArea() { return scale * scale; }
}
class Circle extends Form {
Circle(double s) { super(s); }
double getArea() { return Math.PI * scale * scale; }
}
class Triangle extends Form {
Triangle(double s) { super(s); }
double getArea() { return scale * (scale * Math.sqrt(3) / 4); }
}
優勢:
new Sqaure() 找不到類別)getArea()
abstract class Form {
double scale;
Form(double s) { scale=s; }
double getArea() {
if (this instanceof Square) return scale*scale;
else if (this instanceof Circle) return Math.PI*scale*scale;
else return scale*(scale*Math.sqrt(3)/4);
}
}
class Square extends Form { ... }
class Circle extends Form { ... }
class Triangle extends Form { ... }
雖保留編譯期類型檢查與不可變性,但 instanceof 鏈重蹈 if-else 覆轍:新增子類需修改 getArea(),擴充性同樣差,且分散邏輯更難維護。
語言提供避免此類錯誤的最佳資源——記得使用它們。將概念上不同的資料類型建模為獨立類別,讓編譯器成為你的第一道防線。