動態

詳情 返回 返回

Flutter/Dart第06天:Dart基礎語法詳解(變量) - 動態 詳情

Dart官網文檔:https://dart.dev/language/variables

重要説明:本博客基於Dart官網文檔,但並不是簡單的對官網進行翻譯,在覆蓋核心功能情況下,我會根據個人研發經驗,加入自己的一些擴展問題和場景驗證。

Dart中的變量

變量是一個對象的引用,引用名就是變量的名稱;就算引用是null的變量也一樣。

變量有3種定義方式:var關鍵字,顯示類型Object/dynamic類型。

var varName = 'Tom';
String strName = 'Tom';
Object objName = 'Tom';
dynamic dynName = 'Tom';

最佳實戰:對於局部變量,優先使用var關鍵字,其次為顯示類型,再次為Object,不推薦使用dynamic(原因:容易引發運行時錯誤,後續的學習會講到)。

當使用var關鍵字定義的變量,Dart會根據值內容,推導出變量的實際類型,如上訴代碼varName變量最終為String類型。

Null safety空安全

Dart語言強制健壯空安全。空安全可以防止意外使用null而導致的錯誤(還記得在Java編程中,在很多對象使用的地方,我們都需要進行null判斷,以防止NPE的發生)。Dart在編譯期間,就會進行null潛在錯誤檢測分析,從而防止這些錯誤的發生(注意:並不是所有場景都能檢測分析到,後面章節會講到)。

代碼樣例:如下代碼,變量strName的默認值為null,在其他編程語言如Java語言中,下面的代碼是合法的,但是在運行時,當執行strName.length時會引發NPE異常;但是在Dart語言中,以下代碼是非法的(無法編譯),因此阻止發生NPE等這些潛在的錯誤。

String strName;
print(strName.length)

Dart為了強制執行空安全,有3個關鍵改變

  1. 如果明確希望某個變量、參數或者其他組件是可以為null的,那麼需要在類型後面增加一個?標識:
String? name  // `name`的值可能為`null`, 或者為某個字符串
String name   // `name`的值只能是某個字符串,不能為`null`
  1. Dart的變量在使用之前,必須被初始化。可空變量(含有?標識)的默認值為null,即默認初始化為null,因此無需顯示的賦值初始化;而非空變量因沒有默認值,因此必須顯示賦值初始化。
  2. 禁止直接訪問可空類型的屬性或者表達式方法,包括訪問null對象的hashCodetoString()方法(記住:Dart中一起皆對象,null也是),也會引發錯誤。

Dart空安全通過以上3個關鍵改變,保證null潛在錯誤在代碼編寫階段就能被前置發現,而不是等到代碼運行時。

變量默認值

第2章節中,其實已經提到一點:任何變量在使用之前,必須被初始化;可空變量因為默認值為null,因此可無需顯示初始化;非空變量必須顯示初始化。

特別注意:非空變量只需要確保它在被使用時已經初始化即可,而不是必須在申明的時候。如下代碼,變量lineCount在申明時並未初始化,但是在print被使用時,Dart語言檢測到它已經被初始化了,因此如下代碼是空安全有效代碼。

// 申明:未被初始化
int lineCount;

if (weLikeToCount) {
  lineCount = countLines();
} else {
  lineCount = 0;
}

// 使用:Dart檢測到已經被初始化,因此是可以使用
print(lineCount);

late延遲變量

頂級變量和類變量會延遲初始化,當第一次使用到這些變量時,初始化代碼的邏輯才會被執行(即:延遲初始化)。

在大多數情況下,Dart可以檢測並分析一個非空變量在使用時已經初始化,但是在有些場景下,Dart無法檢測分析或者檢測分析會失效。最常見的2種場景:頂級變量實例變量,Dart無法確定它們在被使用時是否已經被初始化了。

如果我們明確一個非空變量在被使用之前能完成初始化(但Dart卻無法檢測到),可通過增加late關鍵字,告訴Dart該變量為延遲變量,在被使用之前確保能被初始化。當然,對於late延遲變量,在被使用時卻並沒有初始化,那麼使用它同樣會導致運行時錯誤

late String description;

void main() {
  description = 'Feijoada!';
  print(description);
}

late延遲變量主要處理以下2種場景:

  1. 這些變量並不是必須的,同時初始化它們非常耗時或者浪費資源(如網絡交互等)。
  2. 在初始化實例變量時,需要用到實例本身。

代碼樣例:如下代碼,當變量temperature在第1次被使用時,才會被調用readThermometer()方法,即該方法僅僅被調用1次。

late String temperature = readThermometer();

final變量和const常量

最佳實踐:如果我們明確一個變量在被初始化之後,它的引用值再也不會變化,那麼使用final或者const修飾,而不是使用var者顯示類型

  1. const變量隱式為final變量
  2. const變量是編譯期的常量變量
  3. 實例變量可以是final變量但不能是const變量(原因:實例在運行時才能確定,因此其變量無法作為編譯期常量)
final name = 'Bob';
final String nickname = 'Bobby';
const bar = 1000000;
const double atm = 1.01325 * bar;

特別注意:const不僅可以申明如上代碼的常量變量,它也可以用於申明創建常量值,也可以用於申明創建常量值的構造器(還記得第2天學習內容:const構造函數?);同時,任何變量,都可以有常量值。

// 1. 常量值
var foo = const [];
final bar = const [];
const baz = []; // 等同:`const []`

// 2. 非final/const變量,它的常量值可以更新
foo = const [1, 2, 3];

特別注意:常量的定義,可以包含類型檢測、類型轉換、集合過濾和集合展開操作符。

// `i`是一個`Object`類型常量,它的值是int數字值
const Object i = 3;

// 1. 類型轉換
const list = [i as int];

// 2. 類型檢測和集合過濾
const map = {if (i is int) i: 'int'};

// 3. 類型檢測、集合過濾、集合展開
const set = {if (list is List<int>) ...list};

final變量const常量總結:

  1. final變量不可修改,但是它的值是可以更新
  2. const常量不可修改,同時它的值也不可以更新,即它的值是不可變的(immutable)。
void main() {
  // 1. final變量
  final finalList = [1, 2, 3];
  print('1.1 final變量: $finalList');

  // 輸出:1.1 final變量: [1, 2, 3]

  finalList
    ..add(4)
    ..add(5);
  print('1.2 final變量: $finalList');

  // 輸出:1.2 final變量: [1, 2, 3, 4, 5]

  // 2. const常量
  const constList = ['a', 'b', 'c'];
  print('2.1 const常量: $constList');

  // 輸出:2.1 const常量: [a, b, c]

  constList
    ..add('c')
    ..add('d');
  print('2.2 const常量: $constList');

  // 錯誤:
  // Unhandled exception:
  // Unsupported operation: Cannot add to an unmodifiable list
  // #0      UnmodifiableListMixin.add (dart:_internal/list.dart:114:5)
}

我的本博客原地址:https://ntopic.cn/p/2023100101


Add a new 評論

Some HTML is okay.