Dart 如何優雅的避空

AndroidTraveler發表於2019-02-23

Dart 如何優雅的避空

前言

對於每一個程式設計師來說,空指標異常應該是基本都會遇到過的異常,而且這個異常出現的概率還比較大。

但是,空指標異常又是最容易解決的異常,因為只要加個非空判斷就可以避免了。

本篇通過對比一般非空判斷和 dart 特有的語法糖告訴你如何使用 dart 進行優雅的避空。

目錄

Dart 如何優雅的避空

1. dart 線上編輯器

一般一些簡單的 dart 測試我們可以直接用線上編輯器來做測試和驗證。

下面給大家介紹的兩個都是官網的。

dart 線上執行器主頁版: ?
www.dartlang.org/guides/get-…

dart 線上執行器全屏版: ?
dartpad.dartlang.org/null

其中全屏版就是在主頁版裡面點選全屏按鈕就開啟了。

所以可以認為是一樣的。

但是筆者使用起來的不同如下,大家可以根據自己的感受選擇。

主頁版:
優點:執行輸出結果較全屏版快。
缺點:輸出結果區域較小,超出需要滑動檢視。

全屏版:
優點:輸出結果區域大。可以直觀看到結果。 缺點:執行輸出結果較主頁版慢。

2. dart ?.

dart 語法糖 ?.

它的意思是左邊如果為空返回 null,否則返回右邊的值。

A?.B
如果 A 等於 null,那麼 A?.B 為 null
如果 A 不等於 null,那麼 A?.B 等價於 A.B

Sample:

void main() {
  Animal animal = new Animal('cat');
  Animal empty = null;
  
  //animal 非空,返回 animal.name 的值 cat
  print(animal?.name);
  //empty 為空,返回 null
  print(empty?.name);
  
  //animal 非空,可以直接訪問 animal.name 的值 cat
  print(animal.name);
  //empty 為空,丟擲異常
  print(empty.name);
}

class Animal {
  final String name;
  Animal(this.name);
}
複製程式碼

大家拷貝程式碼然後替換線上編輯器的內容,執行後會看到如下輸出:

cat
null
cat
Uncaught exception:
Cannot read property 'get$name' of null
複製程式碼

可以看到假設左邊不為空,不管是使用**?.還是直接用我們熟悉的.訪問變數都是沒問題的。
但是如果左邊為空,使用
?.會返回null**。但是直接使用**.**會直接丟擲異常。

3. dart ??

dart 語法糖 ??

它的意思是左邊如果為空返回右邊的值,否則不處理。

A??B
如果 A 等於 null,那麼 A??B 為 B
如果 A 不等於 null,那麼 A??B 為 A

以上面為例子,假設我們上面要求當 empty 為空時,預設值輸出 unknown。

那麼可以修改如下:

//empty 為空,返回 null
print(empty?.name);
複製程式碼

改為

//empty 為空,本來要返回 null,由於有 ??,返回 unknown
print(empty?.name??'unknown');
複製程式碼

這樣就不會返回 null 而是返回 unknown。

同樣的大家可以試下返回 cat 的語句如果加上這個會怎樣,可以預見是不會改變的。

4. dart ?. ?? 優雅所在

這邊舉例說明下使用 ?. ?? 語法糖和不使用的對比。

void main() {
  C c = new C('Case 1');
  B b = new B(c);
  A a = new A(b);
  
//   C c = new C(null);
//   B b = new B(c);
//   A a = new A(b);
  
//   C c = new C('Case 2');
//   B b = null;
//   A a = new A(b);
  
  //直接使用.來最終獲取 c 的變數 value
  if (a != null && a.bMember != null && a.bMember.cMember != null) {
    print(a.bMember.cMember.value);
  } else {
    print(null);
  }
  
  //直接使用.來最終獲取 c 的變數 value,為空時返回 unknown
  if (a != null && a.bMember != null && a.bMember.cMember != null) {
    String value = a.bMember.cMember.value;
    if (value == null) {
      value = 'unknown';
    }
    print(value);
  } else {
    print('unknown');
  }
  
  //dart 使用?.來最終獲取 c 的變數 value
  print(a?.bMember?.cMember?.value);
  //dart 使用?.來最終獲取 c 的變數 value,為空時使用 ?? 返回 unknown
  print(a?.bMember?.cMember?.value??'unknown');
}

class A {
  final B bMember;
  A(this.bMember);
}

class B {
  final C cMember;
  B(this.cMember);
}

class C {
  final String value;
  C(this.value);
}
複製程式碼

這裡面有三個 case,另外兩個 case 暫時註釋掉。

這三個 case 的結果分別為:

Case 1
Case 1
Case 1
Case 1
複製程式碼
null
unknown
null
unknown
複製程式碼
null
unknown
null
unknown
複製程式碼

可以看到 dart 的語法糖很優雅,一行全搞定。

5. print 方法遇到 null

下面這個例子:

void main() {
    String a = null;
    print('exception='+a);
}
複製程式碼

你覺得結果是 exception=null 嗎?

Dart 如何優雅的避空

結果是

Uncaught exception:
Invalid argument: null
複製程式碼

原因是因為 print 裡面連線的必須是字串。

因為這裡 a 確實是字串,所以編輯器沒有報錯。

假設這裡 a 為一個物件 A 的變數,會報如下提示:

The argument type 'A' can't be assigned to the parameter type 'String'.
複製程式碼

那我們怎麼處理?

有兩種方法。

方法一:

void main() {
    String a = null;
    print('exception='+'$a');
}
複製程式碼

方法二:

void main() {
    String a = null??'null';
    print('exception='+a);
}
複製程式碼

注意下面的寫法是不行的,原因是 ?? 優先順序沒有 + 高。需要加小括號。

void main() {
    String a = null;
    print('exception='+a??'null');
}
複製程式碼

6. 牛刀小試

知識學以致用才能夠鞏固。

因此這邊出了小題目給大家測試是否完全掌握本篇內容。

答案組成了支付寶口令紅包哦~

微信公眾號回覆「牛刀小試」獲取題目。

或者直接點選選單欄目錄->牛刀小試獲取。

溫馨提示:
如果你輸入 3 次還是提示錯誤(錯誤過多口令紅包會暫時不可用哦),有兩種情況。

第一種就是答案錯了。

第二種就是領取完了。

答案會在紅包領取完之後或一天之後將題目替換為題目+答案。

因為是非同步的,所以不一定實時更新哦~

更多閱讀:
Flutter 即學即用系列部落格——01 環境搭建
Flutter 即學即用系列部落格——02 一個純 Flutter Demo 說明
Flutter 即學即用系列部落格——03 在舊有專案引入 Flutter
Flutter 即學即用系列部落格——04 Flutter UI 初窺 Flutter 即學即用系列部落格——05 StatelessWidget vs StatefulWidget

Dart 如何優雅的避空

相關文章