# [Extension types](https://dart.dev/language/extension-types) referance: [aa](https://blog.csdn.net/ZuoYueLiang/article/details/136129351) 將現有的類型做一個輕量的新包裝, 主要用於提供類型安全性、新增邏輯,並提升程式的可讀性與設計靈活性。語法: ``` extension type NewTypeName(被包裝的基類 instanceField){ // 新增方法, getter, setter, operator // 實例屬性只有 instanceField } ``` ``` // 可以泛型 extension type E<T>(List<T> elements) { // ... } ``` ### 封裝新的邏輯: 符合id條件的string representation type = String = 表示類型 representation type declaration = (String id) extension type = UserId = 擴展類 ``` extension type UserId(String id) { // id.length=10 && 首字母是英文, 其餘為數字 bool isValid() { RegExp pattern = RegExp(r'^[a-zA-Z]\d{9}$'); return pattern.hasMatch(id); } // 首字母大寫 String get value=>id[0].toUpperCase() + id.substring(1); void repl()=>print('UserId: ${value}'); } void main() { var id = UserId('f121491445'); print(id.value); // F121491445 print(id.isValid()); // true id.repl(); // UserId: F121491445 } ``` ### 提高類型安全 防止錯誤類型的使用。例如,定義 Email 和 Username 擴展類型: ``` extension type Email(String value) { bool isValid() => value.contains('@') && value.contains('.'); } extension type Username(String value) { bool isValid() => value.length >= 3; } ``` 這樣,Email 和 Username 會有明確的語意,避免誤用。 ### Constructors * 擴充類型可以有建構函數,而擴充方法則不能 * E(int i)本身就是隱式的default constructor * 其他constructor必須在其初始化清單或形式參數中使用this.i來初始化表示物件的實例變數 ``` // int i: representation field, 只能有一個 extension type E(int i) { // 形式參數 E.n(this.i); // 初始化清單 E.m(int j, String foo) : i = j + foo.length; // 或this.i = j + foo.length; } void main() { E(4); // 隱式的 default constructor. E.n(3); // Named constructor. E.m(5, "Hello!"); // Named constructor with additional parameters. } ``` * 如果想要有非隱式的 default constructor, 可以named E(int i) ``` // E._(int it): named constructor // const E._(int it): 常數建構函數 extension type const E._(int it) { // default constructot E() : this._(42); // 形式參數 初始化 E.otherName(this.it); } void main() { print(E()); const E._(2); // 私有建構函數, lib外呼叫不到 E.otherName(3); } ``` * 只希望lib外部呼叫者用String來初始化 ``` // this._(int i)是私有建構函數, 外部無法呼叫 extension type E._(int i) { E.fromString(String foo) : i = int.parse(foo); } ``` ### Members * 擴展類型中的成員指的是: method, getter, setter, operator, 不允許abstract method ``` extension type PosiNum(int value) { // Operator: PosiNum operator +(PosiNum other) => PosiNum(value + other.value); // Getter: PosiNum get myNum => this; // Method: bool isValid() => !value.isNegative; } ``` ### Implements **擴展類A** 只能 implements 它的 **表示類型B** 或 **B的超類**, 或是其他也有包裝相同 **表示類型B** 的 **其他擴展類** implements 過來的成員, 只要沒有 **@redeclare**, 都可以直接使用(不用真的實作) 擴展類只能implements: * 它的表示類型int ``` extension type NumberI(int i) implements int{ // 'NumberI' can invoke all members of 'int', // plus anything else it declares here. } ``` * 它的表示類型的超類 ``` extension type Sequence<T>(List<T> _) implements Iterable<T> { // Better operations than List. } // Object 是 int 的超類 extension type Id(int _id) implements Object { // Makes the extension type non-nullable. static Id? tryParse(String source) => int.tryParse(source) as Id?; } ``` * Another extension type that is valid on the same representation type. ``` extension type Email(String e) { // ... } extension type UserId(String id) implements Email{ // .... } ``` **[@redeclare](https://dart.dev/language/extension-types#redeclare)** (不能用, 有錯誤) 可以使用 @redeclare 註解告訴編譯器我有意選擇使用與超類型成員相同的名稱來重新宣告implements過來的超類成員. ``` extension type MyString(String _) implements String { // Replaces 'String.operator[]' // @redeclare Error // @override ok int operator [](int index) => codeUnitAt(index); } void main() { MyString s = MyString('mdc'); print(s[0]); // 109 String ss='mdc'; print(ss[0]); // m } ``` ### 用例 擴充類型有兩個同樣有效但本質上不同的核心用例: 1. 為現有類型提供擴充接口 當擴充類型implements表示類型後, 表示類型的所有成員都可以成為擴充類型的成員(透明性) ``` extension type NumberT(int value) implements int { // Doesn't explicitly declare any members of 'int'. // 所以int的所有成員都可以被叫用 NumberT get i => this; } void main() { // All OK: 透明性使擴充類型可以完全使用int的成員 var v1 = NumberT(1); print(v1.runtimeType); // int int v2 = NumberT(2); print(v2.runtimeType); // int var v3 = v1.i - v1; print(v3.runtimeType); // int var v4 = v2 + v1; print(v4.runtimeType); // int var v5 = 2 + v1; print(v5.runtimeType); // int //v2.i;// Error: getter .i 不存在於 int類型 中 (v2 as NumberT).i; } ``` 2. 為現有類型提供不同的介面 不透明的擴充類型(不implement其表示類型)被靜態地視為**全新類型**,與其表示類型不同。無法將其指派給其表示類型,並且它不會公開其表示類型的成員。 ``` extension type NumberE(int value) { NumberE operator +(NumberE other) => NumberE(value + other.value); NumberE get next => NumberE(value + 1); bool isValid() => !value.isNegative; } void testE() { var num1 = NumberE(1); int num2 = NumberE(2); // Error: Can't assign 'NumberE' to 'int'. num1.isValid(); // OK: Extension member invocation. num1.isNegative(); // Error: 'NumberE' does not define 'int' member 'isNegative'. var sum1 = num1 + num1; // OK: 'NumberE' defines '+'. var diff1 = num1 - num1; // Error: 'NumberE' does not define 'int' member '-'. var diff2 = num1.value - 2; // OK: Can access representation object with reference. var sum2 = num1 + 2; // Error: Can't assign 'int' to parameter type 'NumberE'. List<NumberE> numbers = [ NumberE(1), num1.next, // OK: 'next' getter returns type 'NumberE'. 1, // Error: Can't assign 'int' element to list type 'NumberE'. ]; } ``` ### 類型注意事項 1. Extension type 是一種在 編譯時 定義的語法結構。 * 它可以用來為基礎類型(如 int, String, double 等)提供額外的行為或功能,類似於「類型安全的包裝器」。 * 在編譯階段,Dart 確保這些包裝器的使用遵循你定義的類型規則。 2. 擴展類型運行時沒有痕跡(No trace at run time) * Extension type 不會在 運行時 引入新的對象或結構。 * 它只是對現有基礎類型(例如 int 或 String)的一層語法上的包裝,運行時的對象仍然是基礎類型。 * Dart 編譯器會在運行時移除這些包裝器,直接將其視為基礎類型的操作。 ``` extension type MyInt(int value) { int doubleValue() => value * 2; } void main() { // 在運行時,MyInt 的實例其實並不存在。 // myInt 實際上就是一個普通的 int, // Dart 編譯器會將對 MyInt 的操作優化為直接操作基礎值 5。 MyInt myInt = MyInt(5); print(myInt.doubleValue()); // Output: 10 運行時只有doubleValue()這個邏輯, 本身是 int print(myInt); // Output: 5 (仍然是 int) assert(myInt is int); // true assert(myInt is int); // true } ``` ``` extension type NumberE(int value) { NumberE operator +(NumberE other) => NumberE(value + other.value); NumberE get next => NumberE(value + 1); bool isValid() => !value.isNegative; } void main() { var n = NumberE(1); // Run-time type of 'n' is representation type 'int'. if (n is int) print(n.value); // Prints 1. // Can use 'int' methods on 'n' at run time. if (n case int x) print(x.toRadixString(10)); // Prints 1. switch (n) { case int(:var isEven): print("$n (${isEven ? "even" : "odd"})"); // Prints 1 (odd). } } ``` ``` void main() { int i = 2; // 直接設定為 表示類型 if (i is NumberE) print("It is"); // Prints 'It is'. if (i case NumberE v) print("value: ${v.value}"); // Prints 'value: 2'. switch (i) { case NumberE(:var value): print("value: $value"); // Prints 'value: 2'. } } ``` 3. 為什麼這樣設計? * 性能: 不引入額外的運行時開銷,使程序運行效率更高。 運行時仍然是基礎類型,沒有內存分配或額外的對象包裝。 * 類型安全: 在編譯時提供強類型檢查,確保正確使用擴展類型。 避免誤用其他基礎類型作為擴展類型的問題。 * 靈活性: 提供類型安全的額外功能,同時保留基礎類型的高效性。 4. 限制 由於運行時沒有 extension type 的痕跡,無法存儲額外的狀態:不能像真正的類那樣新增字段或屬性。 ### 結論 * Extension type 只是 Dart 編譯器在編譯時提供的一層語法保護,用於確保類型安全和功能擴展。 * 在運行時,它實際上不會引入任何新的對象或類型結構,仍然直接操作基礎類型。 * 這種設計方式結合了類型安全與高效運行的優勢。