Dart 健全型別系統(Sound Type System)
核心定義
Dart 是一套健全靜態型別語言,透過「靜態編譯檢查 + 執行期驗證」雙重機制保障安全:變數在執行階段存放的值,必定與其靜態宣告的型別相符。
型別規則強制生效,但型別註解可省略,交由編譯器自動推論;
編譯時的靜態分析可提前攔截絕大多數型別錯誤。
何謂健全性(Soundness)
程式永遠不會進入非法型別狀態;若某運算式靜態型別為 String,執行時只能取得字串,不會出現其他型別。同類型語言:Java、C#。
- 編譯期攔截:
int a = "abc";直接報錯; - 執行期攔截:使用
as強制轉型但型別不吻合時,會拋出例外。
四大優勢
編譯階段就暴露型別錯誤,避開難以重現的上線型別崩潰;
程式可讀性更高,型別不會「欺騙開發者」;
易於維護,修改一處程式碼,型別檢查會標示所有受影響區段;
AOT 編譯效率更佳,型別資訊協助編譯器產生更高效的機器碼。
範例
動態集合觸發靜態錯誤
// 參數預期接收 List<int>
void printInts(List<int> a) => print(a);
void main() {
// 未宣告泛型,自動推論為 List<dynamic>
final list = [];
list.add(1);
list.add('2'); // 動態清單可混合多種型別
printInts(list);
// 編譯錯誤:List<dynamic> 無法指派給 List<int>
}Code language: Dart (dart)
明確宣告泛型約束
void printInts(List<int> a) => print(a);
void main() {
final list = <int>[]; // 明確指定泛型為int
list.add(1);
// list.add('2'); // 編譯報錯,無法放入字串
list.add(2);
printInts(list); // 通過靜態檢查
}Code language: Dart (dart)
三大核心規則
1 覆寫方法時,回傳型別必須是原型別或其子型別(生產者規則)
父類別方法的回傳值屬於生產者,子類別可回傳更具體的子型別,但不能回傳無關型別。
範例
合法寫法
class Animal {
Animal get parent => Animal();
}
class HoneyBadger extends Animal {
@override
HoneyBadger get parent => HoneyBadger(); // 子型別,合法
}Code language: Dart (dart)
非法寫法
class Root {}
class HoneyBadger extends Animal {
@override
Root get parent => Root(); // 無關型別,編譯報錯
}Code language: Dart (dart)
2 覆寫方法時,參數型別必須是原型別或其父型別
方法傳入參數屬於消費者,子類別只能放寬參數範圍(使用父型別),不能縮限為子型別;縮限會破壞型別安全。
合法範例(參數放寬至 Object)
class Animal {
void chase(Animal a) {}
}
class HoneyBadger extends Animal {
@override
void chase(Object a) {} // Object 是 Animal 的父型別,合法
}Code language: Dart (dart)
非法範例(參數縮限為子類別 Mouse)
class Mouse extends Animal {}
class Cat extends Animal {
@override
void chase(Mouse a) {} // 縮限參數型別,觸發靜態錯誤
}
void main() {
Animal cat = Cat();
cat.chase(Alligator()); // 理論上允許傳入 Alligator,造成型別不安全
}Code language: Dart (dart)
規則 3:不可將動態泛型集合直接指派給明確泛型集合
List<dynamic>、List<Dog> 無法直接指派給 List<Cat>,會觸發編譯錯誤;不同泛型集合之間不存在隱含轉換。
class Cat {}
class Dog {}
void main() {
List<Cat> foo = <dynamic>[Dog()]; // 靜態編譯錯誤
List<dynamic> bar = <dynamic>[Dog(), Cat()]; // 合法,動態清單可混合多種型別
}Code language: Dart (dart)
執行期型別驗證
靜態檢查無法覆蓋全部場景,執行階段會額外執行型別校驗,型別不匹配時拋出例外。
泛型強制轉型於執行期報錯
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
void main() {
List<Animal> animals = <Dog>[Dog()];
List<Cat> cats = animals as List<Cat>;
// 執行期例外:無法將 List<Dog> 強制轉型為 List<Cat>
}Code language: Dart (dart)
dynamic 隱含向下轉型(執行期校驗)
將 dynamic 變數指派給明確型別會觸發隱含轉換,於執行階段校驗型別,不吻合則拋錯。
int assumeString(dynamic object) {
String string = object; // 執行期檢查 object 是否為 String
return string.length;
}
void main() {
assumeString("test"); // 正常執行
assumeString(123); // 執行期錯誤:int 無法轉換為 String
}Code language: Dart (dart)
關閉隱含轉換(嚴格型別模式)
在 analysis_options.yaml 開啟嚴格轉換規則,禁止 dynamic 隱含向下轉型
analyzer:
language:
strict-casts: trueCode language: JavaScript (javascript)
型別推論 Type Inference
Dart 可自動推論變數、泛型、方法回傳值的型別;資訊不足時預設推論為 dynamic。
一般變數推論
// 自動推論為 Map<String, Object>
var arguments = {'argA': 'hello', 'argB': 42};
var x = 3; // x 推論為 int
x = 4.0; // 報錯,int 無法存放 double
num y = 3; // 手動宣告 num,可存放 int / double
y = 4.0; // 合法Code language: Dart (dart)
泛型建構子 / 集合推論
List<int> listOfInt = []; // 自動推論為 <int>[]
var listOfDouble = [3.0]; // 推論為 List<double>
var ints = listOfDouble.map((x) => x.toInt());
// x 推論為 double,回傳 int → ints 推論為 Iterable<int>Code language: Dart (dart)
健全型別系統