2019年8月24日 星期六

dart 建構式與函式參數

在Dart 語法中, 函式並不支援函式同名共存(function overloading),類型建構函式(建構式主名稱與類型名稱相同的方法 )自然也不支援構名共存(constructor overloading), 但可以用句點附名方式產生帶(副)名建構式(稱為 named constructor), 加以共存多類型建構式, 當然每個建構式名稱(主名加副名)必須唯一不得重複. 一般建構式會返回類型物件, 因此不用 return. 當建構式名稱與類型完全同名時, 就稱為內定建構式(default construct). 另外一種特殊的建構式稱為工廠建構式(factory constructor), 除了取代一般建構式外, 他必須 return 指定返回物件, 或是用來初始化子代繼承, 但返回的物件必須是該類型或是子類型的物件, 也就是說利用內部現有建構式重組後返回物件. 另外建構式的主名稱與類型的名稱必須相同. 針對 factory  的建構式還有一項特異功能甚至可以將自身物件加以包裝重組與運用, 例如讓其他程序先去處理,等結束後才返回, 或是留在永久迴圈當作主程式, 建構式經由類型名稱加上成對小括號 ( ) 就能化身產生類型物件(instance). 工廠建構式與一般建構式不同在於: 一般建構式會自行返回物件, 而工廠建構式要用 return 手動返回物件, 因為同名建構式無法並存, 因此用一般建構式或工廠建構式, 兩者只能擇一使用.

針對函式的參數, 可以用成對中括號 [ ] 作為位置可省略參數(optional position parameters), 或是用成對大括號 { } 作為帶名可省略參數(optional named parameters), 當有多個參數時, 用逗後加以分開. 所謂位置可省略參數, 顧名思義就是後續參數可以省略, 一旦指定了位置, 相對位置就是特定位置的參數. 所謂帶名可省略參數,同樣都是後續參數可以省略, 一旦要指定, 就必須用同參數名稱加冒號方式去指定該參數值, 且不受位置所影響, 若位置參數中沒用中括號或是大括號括住, 就表示輸入的位置參數是必要的, 另外參數搭配等於符號 = 可以指定預設值. 底線 _ 是一個可以當作合法命名的識別字元, 加以運用時, 若把他放在識別變數的第一個字元, 就代表著私有(private)變數的意義, 只有在該函式庫內可以使用, dart 類型中並沒有 private 或public 或 protected 區域的分類.
class Myclass {
       int a;  // 特性 a
       int b;  // 特性 b
       Myclass.a1(this.a, [this.b=0])  ;  // 一般帶名建構式  .a1
       Myclass._a1([this.a=0, this.b=0]); //另一帶名建構式 ._a1
       Myclass.b1(this.b, {this.a=0});   //另一帶名建構式  .b1
       Myclass._b1({this.a=0, this.b=0}); //另一帶名建構式 ._b1
       factory Myclass.a ( ) { // 透過帶名建構式產生物件的工廠副名建構式 .a
             return  Myclass._a1(1, 2);
       }
      factory Myclass.b ( ) {// 另一透過帶名建構式產生物件的工廠副名建構式 .b
             return  Myclass._b1(b:2,  a:1);
       }
      //  Myclass({this.a=3, this.b=4}); // 內定一般建構式完成後才產生物件
      factory   Myclass( ) { // 內定工廠建構式比較彈性, 可以針對類型物件再處理
           final obja  = Myclass.a( );  // 先產生本身物件
           final objb  = Myclass.b( );  // 還可以再一次產生物件, 甚至其他類型物件
           final obj    = Myclass._a1(obja.a, objb.b);// 物件再處理.
           runApp(obj); // 交給其他程式處理
           return obj;   //最後才返回該類型物件
      }
}
runApp(Myclass obj) {
    // ...
 }
main() {
  final   obja = Myclass.a( );   // 使用副名 .a 工廠建構式 (factory  .a constructor)
  final   objb = Myclass.b( );   // 使用副名 .b 工廠建構式 (factory .b constructor)
  final   obj    = Myclass( );     // 使用內定工廠建構式(default factory constructor)
}

dart 有兩個方便使用的方法分別是 get propertyName => _field; 及  set propertyName(value)=> _field=value; 實際上是一個仲介橋樑, 將主角(_field)隱藏起來, 或者說它是一個右進左出的方法, 讀取或寫入 propertyName 時就像一般的 property 一樣, 完全不需要加上小括號 ( ), 當寫入時用等於符號 = 指定數值:
    int  _field;  // 後台主角
    get object     => _field;  // 讀取主角的方法直接用 object
    set object(value) => _field = value; // 寫入主角的方法用 object = 指定數值
    main( ) {
         object = 3; // 寫入 3
         print(object); // 讀取 object
   }
工廠建構式(factory constructor)用帶名建構式(named constructor)搭配static (靜態物件)就可在類型內創造出一次性(唯一)共享物件(singleton), 有一點要注意的是: 靜態物件的實體, 實際上並不是放在類型裏面, compiler 最終會將它移到整體區域去初始化(就如同 c++ 靜態物件一樣, 若未指定數值內定就是 null), 宣告成靜態物件(包含方法)等同宣告帶名的整體區域物件, 因此靜態物件可以取用同樣宣告成靜態物件, 但靜態物件並不能使用 this 這個(指標)物件(因為實體不在類型裏面), 換句話說靜態方法無法取用類型裏面任何資源. 反過來, 在類型裏面卻可以讀取靜態物件, 主要原因是 compiler 已經將符號做了連結.  透過 get 方法也可方便連結. 另外 dart 語法也支援符號運算重寫(operator override) 的方法, 詳如範例:
class Singleton {  // 物件主角看似 object, 實際是 _field
    final String name; // 化身後的私名稱
    static Singleton _field;//static _field實體由該類型所有物件(instance)共享, 帶名整體區域
    get object => _field;  // 主角連結的是 Singleton._field,  帶名的整體區域物件
    get hashCode => this.hashCode ^ _field.hashCode; // 改寫 == 運算也需改寫 hashCode
    operator == (covariant other) => other is Singleton && other.object==object //運算 ==
    Singleton._(this.name);         // 帶名建構式, 副名 ._
    factory Singleton(String init) => _field == null ? Singleton._(init) :  _field;//工廠建構式
}
main( ) {
   final a = Singleton("A");
   final b = Singleton("B" );
   print(a == b);    // 因為裏面 object 宣告成 static 物件共享, 所以是同一物件
   print(a.name); // 私名稱各異
   print(b.name); // 私名稱各異
}結果:
true
A
B

沒有留言:

張貼留言

使用 pcie 轉接器連接 nvme SSD

之前 AM4 主機板使用 pcie ssd, 但主機板故障了沒辦法上網, 只好翻出以前買的 FM2 舊主機板, 想辦法讓老主機復活, 但舊主機板沒有 nvme 的界面, 因此上網買了 pcie 轉接器用來連接 nvme ssd, 遺憾的是 grub2 bootloader 無法識...