Dart語法之變量聲明與數據類型實例詳解

前言

最近在學習做 flutter 移動端開發。相比 React-Native 開發而言, 使用 Flutter 開發的話要使用 Dart 這門語言,導致學習負擔更重一點。所以針對 Dart 語言的語法和使用做一下匯總。

以下內容參考自 Dart 官方文檔

1.安裝與使用

dart是由google公司開發的一門面向對象的編程語言。主要應用在移動端,配合 flutter 使用。dart2為現階段使用的穩定版本

1.1 安裝

因為學習 dart 大多數是為瞭寫 flutter,所以推薦直接下載 flutter,下載的 flutter 中會帶有 dart 的 SDK。

flutter 推薦去官網進行下載。下載完成後解壓,dart 的 SDK 就在解壓目錄\bin\cache\dart-sdk下。

1.2 在 vscode 中使用

為瞭方便使用,我們可以將 dart 的 SDK 設置在環境變量中,將解壓目錄\bin\cache\dart-sdk\bin的完整路徑設置好,在cmd 中輸入 dart ,有響應就代表設置成功瞭。

然後就是如何在 vscode 中使用dart。為瞭使用 dart,我需要下載兩個插件DartCode Runner,下載完成後創建一個文件main.dart,輸入如下代碼:

 // dart中的代碼需要放入main方法中執行
 main(){
     print('Hello World');
 }

然後右鍵Run Code,如果控制臺成功打印出Hello World證明我們已經能夠在 vscode 中使用 dart 瞭。

2.類型聲明

2.1 變量聲明

在 dart 中有很多聲明變量的關鍵字,可以使用能接受任何類型值的變量申明(類似 JavaScript),也可以使用隻能接受固定類型值的變量聲明(類似 JAVA)。

2.1.1 var

類似於JavaScript中的var,它可以接收任何類型的變量,但最大的不同是 dart 中var變量一旦在聲明時被賦值(除瞭被賦值為 null,因為初始化的時候所有的值都為 null),類型便會確定,則不能再改變其類型,如:

 var t = "hi world";
 // 下面代碼在dart中會報錯,因為變量t的類型已經確定為String
 // 類型一旦確定後則不能再更改其類型
 t = 1000;

💡 對於前端人員來說,其實看作是 TypeScript 的自動推斷類型的功能就好。

但是如果一開始沒有直接賦值,而是隻定義,那麼變量的類型默認會是dynamic類型,也就說和 JavaScript 中的聲明的變量一樣的用法瞭。

 var t;
 t = "hi world";
 // 下面代碼在dart中不會報錯
 t = 1000;

2.1.2 const 和 final

如果從未打算更改一個變量,那麼使用 finalconst,不是var,也不是一個單獨的類型聲明(類型聲明也是可以更改值的,當使用類型聲明定義變量時,可以直接在前面加上constfinal關鍵字使其變成常量,但是我們一般建議省略類型聲明)。

使用constfinal聲明的變量都隻能被設置一次,兩者區別在於:

const常量是一個編譯時常量(就是說必須要是一個在程序編譯時就完全固定的常量),final常量不僅有const的編譯時常量的特性,最重要的是它是運行時常量,final是惰性初始化的,即在第一次使用時才會初始化。

 // 可以省略String這個類型聲明
 final str = "hi world";
 final String sstr = "hi world"; 
 const str1 = "hi world";
 const String sstr1 = "hi world";
 // 運行時常量在運行時才會被賦值
 // 獲取當前時間,因為是動態獲取的,所以不能通過const聲明
 final t = new DateTime.now(); // OK
 const t1 = new DateTime.now(); // Error

註意:

雖然 final 是運行時常量,第一次被賦值也必須是在定義的時候賦值。

 final a; // Error
 a = 1;

實例變量可以是 final,但不能是 const。(實例變量定義在對象一級,它可以被類中的任何方法或者其他類中的方法訪問,但是不能被靜態方法訪問)

 class A {}
 main() {
     final a = new A(); // OK
     const b = new A(); // Error 
 }

const 關鍵字不隻是聲明常量變量。還可以使用它來創建常量值,以及聲明創建常量值的構造函數。任何變量都可以賦一個常量值。

 var foo = const [];
 final bar = const [];
 ​
 // 可以從const聲明的初始化表達式中省略const
 const baz = []; // Equivalent to `const []` => const bar = const [];
 // 不能改變const變量的值
 baz = [42]; // Error: Constant variables can't be assigned a value.
 // 可以更改一個非final的非const變量的值,即使它曾經有一個const值
 foo = [1, 2, 3]; // Was const []

有些類提供常量構造函數。要使用常量構造函數創建編譯時常量,請將 const 關鍵字放在構造函數名之前:

 class Person{
     const Person();
 }
 var p = const Person();

2.1.3 dynamic 和 Object

  • Object 是 dart 所有對象的根基類,也就是說所有類型都是Object的子類(包括FunctionNull),所以任何類型的數據都可以賦值給Object聲明的對象。
  • dynamic是與int這樣一樣的類型關鍵詞,改類型聲明的變量也可以賦值任意對象。

💡 dynamicObject相同之處在於,它們聲明的變量可以在後期改變賦值類型(類似使用 JavaScript 或 TypeScript 中的any類型)。

 dynamic t;
 Object x;
 t = "hi world";
 x = 'Hello Object';
 // 下面代碼沒有問題
 t = 1000;
 x = 1000;

dynamicObject不同的是,dynamic聲明的對象編譯器會提供所有可能的組合(也就是相當於就是完完全全的 JavaScript變量),而Object聲明的對象隻能使用 Object 類的屬性與方法,否則編譯器會報錯。

 dynamic a;
 Object b;
 main() {
     a = "";
     b = "";
     printLengths();
 }   
 
 printLengths() {
     // no warning
     print(a.length);
     // warning:
     // The getter 'length' is not defined for the class 'Object'
     print(b.length);
 }

2.1.4 默認值

未初始化的變量的初始值為 null。甚至具有數字類型的變量最初也是 null,因為在 dart 中所有的東西都是對象。

 int lineCount;
 assert(lineCount == null);

註意: 在生產環境中,assert()調用被忽略。在開發環境中當assert(condition)的 condition 條件不為真時拋出一個異常。

2.2 數據類型

我們要清楚的是,dart 中的所有類型的值全都是對象,所以在其他語言中常用的基本類型聲明在 dart 中實質也是類的類型聲明。

2.2.1 Number

Number 總共能使用三種類型:

  • num
  • int
  • double

Dart的數字有兩種形式:

int:根據平臺的不同,整數值不大於64位。在 Dart VM 上,值可以從-263263 - 1。編譯成 JavaScript 的 Dart 使用 JavaScript 代碼,允許值從-253253 - 1

整數是沒有小數點的數。這裡有一些定義整數字面量的例子:

 int x = 1;
 int hex = 0xDEADBEEF;

int 類型指定傳統的(<<, >>)和(&),或(|)位操作符。例如:

 assert((3 << 1) == 6); // 0011 << 1 == 0110
 assert((3 >> 1) == 1); // 0011 >> 1 == 0001
 assert((3 | 4) == 7); // 0011 | 0100 == 0111

double:64位(雙精度)浮點數,由IEEE 754標準指定。

如果一個數字包含一個小數,它就是一個雙精度數。這裡有一些定義雙精字面量的例子:

 double y = 1.1;
 double exponents = 1.42e5;

註: int 和 double 都是 num 的子類型。num 類型包括基本的操作符,如+、-、/和*,還可以在其中找到abs()、ceil()和floor()等方法。(位運算符,如>>,在int類中定義)如果 num 及其子類型沒有要查找的內容,那麼dart:math library可能會有。

以下是如何將字符串轉換成數字的方法,反之亦然:

 // String -> int
 var one = int.parse('1');
 assert(one == 1);
 ​
 // String -> double
 var onePointOne = double.parse('1.1');
 assert(onePointOne == 1.1);
 ​
 // int -> String
 String oneAsString = 1.toString();
 assert(oneAsString == '1');
 ​
 // double -> String
 String piAsString = 3.14159.toStringAsFixed(2);
 assert(piAsString == '3.14');

2.2.2 String

dart 字符串是 UTF-16 編碼單元的序列。可以使用單引號或雙引號創建一個字符串:

 var s1 = 'Single quotes work well for string literals.';
 var s2 = "Double quotes work just as well.";
 var s3 = 'It's easy to escape the string delimiter.';
 var s4 = "It's even easier to use the other delimiter.";

可以使用${expression}表達式的值放入字符串中。如果表達式是一個標識符,可以跳過{}。為瞭獲得與對象對應的字符串,dart 會自動調用對象的toString()方法。

 var s = 'string interpolation';
 ​
 assert('Dart has $s, which is very handy.' ==
        'Dart has string interpolation, ' +
        'which is very handy.');
 assert('That deserves all caps. ' +
        '${s.toUpperCase()} is very handy!' ==
        'That deserves all caps. ' +
        'STRING INTERPOLATION is very handy!');       

註意: ==檢驗兩個對象是否相等。如果兩個字符串包含相同序列的代碼單元,那麼它們是等價的,這點與 JavaScript 類似。

可以使用相鄰的字符串字面量(也可以看做是用空格)+ 運算符連接字符串:

var s1 = 'String ' 'concatenation' " works even over line breaks.";
assert(s1 == 'String concatenation works even over ' 'line breaks.');
var s2 = 'The + operator ' + 'works, as well.';
assert(s2 == 'The + operator works, as well.');

對於創建多行字符串的方法:

  • 使用\n用做換行。
  • 使用帶有單引號或雙引號的三重引號。
var s = 'a \n multi-line string'
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";

如果不想要轉義字符你可以用r前綴創建一個原始字符串

var s = r'In a raw string, not even \n gets special treatment.';
// In a raw string, not even \n gets special treatment.

要註意一點,字符串字面量是編譯時常量,隻要任何內插表達式都是編譯時常量,計算結果為 null 或數值、字符串或佈爾值。

// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];
const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList'; // Error
/*
前三個錯誤因為使用var進行申明的,var聲明的變量之後可以改變值,不符合常量定義,而後一個是因為不符合常量所需類型,也就是不符合null或數值、字符串或佈爾值。即使使用toString()方法也會報錯,這不符合編譯時常量的定義,除非用final
*/
2.2.3 Boolean

為瞭表示佈爾值,dart 有一個名為 bool 的類型。隻有兩個對象具有 bool 類型:佈爾字面量 true 和 false,它們都是編譯時常量。

dart 的類型安全性意味著不能使用if(非booleanvalue)assert(非booleanvalue)之類的代碼。相反,顯式地檢查值,如:

 // Check for an empty string.
 var fullName = '';
 assert(fullName.isEmpty);
 ​
 // Check for zero.
 var hitPoints = 0;
 assert(hitPoints <= 0);
 ​
 // Check for null.
 var unicorn;
 assert(unicorn == null);
 ​
 // Check for NaN.
 var iMeantToDoThis = 0 / 0;
 assert(iMeantToDoThis.isNaN);

2.2.4 List

隻要 List、Set、Map 等的基本用法見 dart 常用庫的使用

Lst API 文檔

也許幾乎所有編程語言中最常見的集合就是數組或有序對象組。在 dart 中,數組是列表對象,所以大多數人把它們叫做列表。

dart 列表字面量看起來像 JavaScript 數組字面量。這是一個簡單的 dart 列表:

 var list = [1, 2, 3];

註意: 上面的代碼分析器推斷該列表具有List<int>類型。如果試圖向此列表添加非整型對象,則分析器或運行時將引發錯誤。

可以獲取列表的長度,並引用列表元素,就像在JavaScript中那樣:

var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);

要創建一個編譯時常量列表(不能再之後改變值),需要在列表字面量之前添加 const(或者直接使用 const 申明變量):

var constantList = const [1, 2, 3];
// OR
const constantList = [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.
// 這裡不會在編譯時報錯,但是運行時會拋出異常

列表類型有許多便於操作列表的方法,在這裡不說,在之後會進行詳細說明。

2.2.5 Set

Set API 文檔

dart 中的集合是一組無序的獨特物品集合。因為集合是無序的,所以不能通過索引(位置)獲得集合的項。

var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
assert(ingredients.length == 3);
// Adding a duplicate item has no effect.
ingredients.add('gold');
assert(ingredients.length == 3);
// Remove an item from a set.
ingredients.remove('gold');
assert(ingredients.length == 2);

使用contains()containsAll()來檢查集合中是否有一個或多個對象:

var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
// Check whether an item is in the set.
assert(ingredients.contains('titanium'));
// Check whether all the items are in the set.
assert(ingredients.containsAll(['titanium', 'xenon']));

交集是一個集合,其項在另外兩個集合中:

var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
// Create the intersection of two sets.
var nobleGases = Set.from(['xenon', 'argon']);
var intersection = ingredients.intersection(nobleGases);
assert(intersection.length == 1);
assert(intersection.contains('xenon'));

2.2.6 Map

Map API 文檔

通常,map 是一個關聯鍵和值的對象。鍵和值都可以是任何類型的對象。每個鍵隻出現一次,但是您可以多次使用相同的值。dart 對 map 的支持是通過 map 字面量和 map 類型來提供的。(可以看做是混入瞭 JavaScript 對象字面量寫法和 JAVA 的 HashMap 鍵值的對象)

var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};
var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

註意:

在上面的代碼中,解析器推斷 gifts 的類型為Map<String, String>,nobleGases 的類型為Map<int, String>。如果您試圖向 map 添加錯誤類型的值,則分析器或運行時將引發錯誤。

dart 中的 map 和 JavaScript 中的對象是有區別的,鍵(key)可以是任意數據類型,並且如果是 String 類型的話不能省略引號,因為在 dart 中這樣會將其解析為一個變量。

const a = '1';
var map1 = {
    true: '123',
    a: '2', // 不加引號的a會被解析為'1'
    b: '2', // 報錯,沒有b變量
    'a': '2'
};

同樣的,在通過 map 的鍵獲取值得時候,不能使用 JavaScript 中常見的點(.)操作符,隻能使用[]操作符,點(.)操作符隻能用在獲取 dart 中通過類生成的對象的屬性或方法中(因為 map 生成的自變量隻是看起來和 JavaScript 中的一樣,實際上還是有很大差別的) 。

同樣可以使用Map構造函數創建對象:

var gifts = new Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = new Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

添加值和檢索值都如同 JavaScript 中那樣進行,不同的是:

如果要獲取的鍵不再 map 中,將會返回一個 null:

 var gifts = {'first': 'partridge'};
 assert(gifts['fifth'] == null);

可以使用.length獲取 map 中元素的個數:

 var gifts = {'first': 'partridge'};
 gifts['fourth'] = 'calling birds';
 assert(gifts.length == 2);

要創建一個編譯時常量的 map 需要在 map 的字面量前加const關鍵字(或直接使用 const 聲明的變量):

 var constantMap = const {
   2: 'helium',
   10: 'neon',
   18: 'argon',
 };
 // OR
 const constantMap = {
   2: 'helium',
   10: 'neon',
   18: 'argon',
 };
 ​
 // constantMap[2] = 'Helium'; // Uncommenting this causes an error.
 // 這裡不會在編譯時報錯,但是運行時會拋出異常

2.2.7 Runes(字符)

在 dart 中,字符是字符串的UTF-32編碼點。

Unicode 為世界上所有的書寫系統中使用的每個字母、數字和符號定義一個唯一的數值。因為 dart 字符串是 UTF-16 代碼單元的序列,所以在字符串中表示32位的 Unicode 值需要特殊的語法。

表示 Unicode 碼點的常用方法是\uXXXX,其中 XXXX 是4位數的十六進制值。例如,心型字符(♥)的編碼為\u2665。要指定大於或小於4位十六進制數字,請將值放在花括號中。例如笑臉表情(😆)的編碼\u{1f600}

String 類有幾個屬性可以用來獲取 runes信息。codeUnitAt 和 codeUnit 屬性返回16位代碼單元。使用字符屬性獲取字符串的字符。

下面的示例說明瞭字符、16位代碼單元和32位代碼點之間的關系:

main() {
  var clapping = '\u{1f600}';
  print(clapping);
  print(clapping.codeUnits);
  print(clapping.runes.toList());
  Runes input = new Runes(
      '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
  print(new String.fromCharCodes(input));
}
//運行效果如下
😀
[55357, 56832]
[128512]
♥  😅  😎  👻  🖖  👍
Process finished with exit code 0

註意: 使用列表操作操作 runes 時要小心。根據特定的語言、字符集和操作,這種方法很容易出錯。有關更多信息,請參見如何在Dart中反轉字符串?

2.2.8 Symbols(符號)

符號對象表示在 dart 程序中聲明的操作符或標識符。一般來說可能永遠不需要使用符號,但是對於按名稱引用標識符的api 來說,它們是非常重要的,因為縮小改變瞭標識符名稱而不是標識符符號。

要獲取標識符的符號,請使用符號文字,符號文字僅為#,後面跟著標識符:

#radix
#bar

註意: 符號常量是編譯時常量。

2.2.9 枚舉類型

枚舉類型可以看做是 dart 對類(class)的一種延伸。

使用enum關鍵字聲明一個枚舉類型:

enum Color { red, green, blue }

枚舉中的每個值都有一個索引 getter,它返回enum聲明中值的從0開始的位置。例如,第一個值有索引0,第二個值有索引1。

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

使用enumvalues常量要獲取枚舉中所有值的列表。

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);

可以在 switch 語句中使用enum,如果 switch 的 case 不處理enum的所有值,將會報一個警告消息:

var aColor = Color.blue;
switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // Without this, you see a WARNING.
    print(aColor); // 'Color.blue'
}

枚舉類型有以下限制:

  • 不能子類化、混合或實現枚舉。
  • 不能顯式實例化一個枚舉。

以上就是Dart語法之變量聲明與數據類型實例詳解的詳細內容,更多關於Dart變量聲明數據類型的資料請關註WalkonNet其它相關文章!

推薦閱讀: