Dart基礎: 構造函數(shù)

Dart基礎: 構造函數(shù)

Dart的構造函數(shù)比較變化多端, 我們可以在不同的場景下合理聲明和調(diào)用一個class的構造函數(shù). 在實際開發(fā)過程中, 其實會遇到各種不同的情況, 需要我們合理使用構造函數(shù). 因此我們需要對構造函數(shù)有充分的了解才可以做出合理的選擇.

聲明 & 調(diào)用

首先假設我們有一個class如下

class Person {
    int id;
    int age;
    String name;
}

--

直接指向變量名的聲明方式

// 參數(shù)分別指向`Person`對應的變量, 都是必填項.
Person(this.id, this.age, this.name);

調(diào)用:

// 調(diào)用的時候, 函數(shù)參數(shù)沒有形式參數(shù)名
var person = Person(0, 18, 'name_of_person');

這種情況下:

  • 可以確保參數(shù)都有傳入.
  • 參數(shù)傳入順序和聲明時候的順序要保持一致.
  • 在缺少參數(shù)的情況下會導致編譯失敗.
  • 沒有明確參數(shù)名稱, 當參數(shù)較多的時候代碼可讀性不高.

--

可選參數(shù)聲明方式1

/*
    用花括號把可選參數(shù)包裹
   同樣是指向`Person`對應的變量, 都是可選參數(shù).
*/
Person({this.id, this.age, this.name});

調(diào)用:

Person(id: 0, name: 'name_of_person', age: 18);
// 或
Person(id: 0);
// 或
Person(name: 'name_of_person');

這種情況下:

  • 有明確的參數(shù)名稱, 在參數(shù)較多的時候仍然可以保持較高可讀性.
  • 傳入?yún)?shù)的順序可以不固定, 只需要對應其參數(shù)名即可.
  • 可以跳過部分參數(shù)不傳入.
  • 有導致對象初始化后部分變量為null.

--

可選參數(shù)聲明方式2

// 用方括號把可選參數(shù)包裹
Person([this.id, this.age, this.name]);

調(diào)用:

Person();
// 或
Person(0, 18);
// 或
Person(0, 18, 'name_of_person');

這種情況下:

  • 調(diào)用的時候不會顯示參數(shù)名.
  • 傳參順序必須按照聲明順序.
  • 傳參的時候不能跳過可選參數(shù)中間的某個參數(shù), 比如上述例子, 不可以傳入idname而跳過age.

--

使用@required關鍵字的可選參數(shù)聲明方式:

// `id`參數(shù)被聲明`@required`, 在調(diào)用的時候必須傳入.
Person({@required this.id, this.name, this.age});

調(diào)用:

Person(id: 0, name: 'name_of_person')
// 如果不傳`id`會引起編譯警告
Person(age: 18, name: 'name_of_person')

這種情況下:

  • 通過編譯器警告提示使用者需要傳入必須的參數(shù).
  • 只能修飾花括號聲明方式下的可選參數(shù).
  • 僅有提示作用, 即使不傳入?yún)?shù)也可以編譯通過.

--

帶默認參數(shù)的聲明方式

// `name`的值默認為'unnamed'
Person({this.id, this.age, this.name = 'unnamed'});

這種情況下:

  • 一個變量在沒有傳參的情況下, 初始化的值就是其默認值.
  • 默認值的聲明方式只可以用在可選型參數(shù)上.
  • 默認值必須是靜態(tài)的.

--

可選參數(shù)聲明的方式看起來很美好, 但是它有一個局限性:

它可以綁定到class的公共變量, 但是無法綁定私有變量.

那我們又想在構造函數(shù)里完成對私有變量的賦值, 又想把這個參數(shù)設置成可選的, 是不是就沒有辦法了?

這個問題是可以解決的, 下面將會提到.

--

以上幾種聲明方式可以混合使用:

Person(this.id, {@required this.age, this.name = 'unnamed'});

調(diào)用:

Person(0, age: 18);
Person(0, name: 'name_of_person', age: 18);

這種情況下:

  • 在聲明時必傳參數(shù)必須都聲明在前.
  • 調(diào)用的時候, 傳參順序: 先按照必傳參數(shù)的聲明順序傳入, 然后才可以傳可選型參數(shù).
  • 可選型參數(shù)的順序可以任意.

--

我們可以發(fā)現(xiàn), 以上幾種聲明方式都是在構造函數(shù)中直接把值綁定到class的變量.

而這樣調(diào)用構造函數(shù), 我們必須在之前就把全部參數(shù)都處理完畢. 這樣往往會導致我們每次創(chuàng)建一個對象前都先做一堆處理, 而我們很多時候并不希望這樣.

我們更希望構造函數(shù)自身可以完成這些處理. dart其實也提供了這種聲明方式:

Person(Map map)
    : this.id = map['id'],
      this.name = map['name'],
      this.age = map['age'];

這樣我們就可以通過傳入一個Map對象的方式實現(xiàn)Person的初始化. 客戶端和服務端數(shù)據(jù)請求之后對http響應數(shù)據(jù)做轉換的時候, 我們面對的就是這種場景.

而上面提到過的私有變量作為可選參數(shù)的問題, 就可以通過這個聲明方式解決. 假設我們增加一個私有變量_deposit, 我們可以用這個形式做聲明:

class Person {
    int id;
    int age;
    String name;
    int _deposit;
    
    /*
      其余幾個參數(shù)會自動綁定, _deposit通過人為手動賦值, 實現(xiàn)required可選參數(shù)
    */
  Person({
    this.id,
    this.age,
    this.name,
    @required int deposit,
  }) : this._deposit = deposit; 
}

--

而實際上, 有時候我們需要做的處理會更加復雜, 用上面這個方式做變量賦值就滿足不了我們了. 這個時候我們還有一種聲明方式:

Person(Map map) {
    this.id = int.parse(map['id'].toString()) ?? 0;
    this.age = int.parse(map['age'].toString()) ?? 0;
    var nameInMap = map['name'];
    this.name = nameInMap is String ? nameInMap : 'unnamed';
}

這種聲明方式可以在構造函數(shù)中做較復雜的數(shù)據(jù)處理, 但是有一種情況下并不好使:

final修飾的變量

如果我們的Person類型做出以下調(diào)整:

class Person {
    final int id;
    int age;
    String name;
}

那么這個id變量就無法在構造函數(shù)的函數(shù)體內(nèi)做賦值操作, 我們只能通過之前的兩種聲明方式來對它賦值.

--

然后這個時候我們可能會碰到一個問題:

如果我們需要通過Map對象的構造函數(shù), 同時也需要直接傳參的構造函數(shù), 我們發(fā)現(xiàn)我們同時聲明這兩種函數(shù), 會得到一個編譯器的警告

The default constructor is already defined.
Try giving one of the constructors a name.

嗯, 對的. 我們上面說了那么多種聲明方式, 在dart語言里面, 都等于是聲明了一個默認構造函數(shù). 而實際上, 其實我們不另外聲明構造函數(shù)的話, dart確實就會給Person提供一個構造函數(shù).

調(diào)用dart默認構造函數(shù):

Person();

當我們需要同時擁有多個構造函數(shù)的時候, 我們需要使用別的方式.

就拿這個Map的創(chuàng)建方式舉例:

// 用`.` + 構造函數(shù)名 的方式聲明構造函數(shù)
Person.fromMap(Map map)
    : this.id = map['id'],
      this.name = map['name'],
      this.age = map['age'];
      
// 或者
Person.fromMap(Map map) {
    this.id = int.parse(map['id'].toString()) ?? 0;
    this.age = int.parse(map['age'].toString()) ?? 0;
    var nameInMap = map['name'];
    this.name = nameInMap is String ? nameInMap : 'unnamed';
}

調(diào)用方式:

// 用`.`的方式調(diào)起構造函數(shù)
Person.fromMap(someMap);

這種情況下:

  • 參數(shù)聲明的規(guī)則和默認構造函數(shù)的規(guī)則是一致的.
  • 可以根據(jù)不同情況選用不同的構造函數(shù).
  • 對于final修飾的變量, 同樣無法在構造函數(shù)的函數(shù)體內(nèi)進行賦值.

選用原則

從上我們已經(jīng)可以看出, dart的構造函數(shù)聲明方式有非常多種. 我的使用選擇主要依從以下幾個原則:

  • 調(diào)用的時候一眼可以看出需要傳遞什么參數(shù).
  • 參數(shù)在2個以上的時候選擇@required的顯式參數(shù).
  • 避免調(diào)用前需要做過多的數(shù)據(jù)處理, 直接傳入其他類型, 在構造函數(shù)中做處理.
  • 通過其他類型初始化的要使用有名字的構造函數(shù).
  • 通過直接變量賦值的使用默認構造函數(shù).

更加簡單地概括, 就是兩個:

  • 利用編譯器和命名做盡量多的提示.
  • 在構造函數(shù)中包攬盡量多的工作.
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學習記錄文檔,今天18年5月份再次想寫文章,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 3,172評論 2 9
  • 一、函數(shù) 函數(shù)用于將代碼結構化,將復雜的問題簡單化,實現(xiàn)根據(jù)功能拆分程序,使得代碼可以實現(xiàn)復用。 Dart 中的入...
    陌問MW閱讀 1,777評論 0 7
  • 函數(shù)和對象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對于任何一門語言來說都是核心的概念。通過函數(shù)可以封裝任意多條語句,而且...
    道無虛閱讀 4,961評論 0 5
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 4,202評論 1 10
  • 函數(shù)只定義一次,但可能被執(zhí)行或調(diào)用任意次。JS函數(shù)是參數(shù)化的,函數(shù)的定義會包括一個稱為形參的標識符列表,這些參數(shù)在...
    PySong閱讀 383評論 0 0

友情鏈接更多精彩內(nèi)容