健全型別系統

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)

健全型別系統

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *