一文了解Dart語法
Flutter應用程式使用Dart語言開發,Dart是面向物件程式語言,由Google於2011年推出,目前最新版本是2.0,為了更好的使用Flutter進行應用開發,本文將詳細介紹Dart語言的語法和特性。
重要概念
在學習Dart之前,先要了解以下Dart相關概念:
-
能夠放在變數中的所有內容都是物件,每個物件都是一個類的例項。甚至於數字、函式和null值都是物件,並且所有物件都繼承自Object類。
-
Dart是強型別語言,但型別標識是可選的,因為Dart可以推斷型別。如果要明確說明不需要任何型別,可以使用特殊型別dynamic標識。
-
Dart支援泛型,如List或List(任何型別的物件列表)。
-
Dart支援頂級函式(例如main函式),以及繫結到類或物件的函式(分別是靜態方法和例項方法)。函式內部也可以建立函式(巢狀函式或本地函式)。
-
Dart支援頂級變數,以及繫結到類或物件的變數(分別是靜態變數和例項變數)。
-
與Java不同,Dart沒有關鍵字public、protected和private。如想設定私有變數或函式,則變數和函式名以下劃線(_)開頭。
-
識別符號可以以字母或下劃線(_)開頭,後跟這些字元加數字的任意組合。
-
Dart有兩個表示式(具有執行時值)和語句(不具有)。 例如,條件表示式條件? expr1:expr2的值為expr1或expr2。 將其與if-else語句進行比較,該語句沒有任何值。 語句通常包含一個或多個表示式,但表示式不能直接包含語句。
-
Dart工具可以報告兩種問題:警告和錯誤。警告只是表明您的程式碼可能無法正常工作,但它們不會阻止您的程式執行。 錯誤可以是編譯時或執行時。 編譯時錯誤會阻止程式碼執行; 執行時錯誤導致程式碼執行時引發異常。
關鍵字
任何語言都有關鍵字,關鍵字是在程式設計時不能使用作為識別符號的單詞。Dart的關鍵字如下:

編碼時應避免使用以上單詞作為識別符號,如果有必要,可以使用帶有上標的單詞作為識別符號:
- 帶有上標1的單詞是上下文關鍵字,僅在特定位置有含義,它們在任何地方都是有效的;
- 帶有上標2的單詞是內建識別符號,它們在大多數地方是有效的,但不能用作為類和型別名稱或作為一個匯入字首;
- 帶有上標3的單詞是與Dart1.0釋出後新增的非同步支援相關的有限的保留字元,不能在任何標記為async,async * 或sync * 的任何函式體中使用await和yield作為識別符號。
變數
變數的定義
1.可以使用var來定義變數,變數的型別可以通過變數值推斷出來
var name = "hi"; //String型別 var age = 18; //int型別 var high = 1.70; //double型別 複製程式碼
如上變數定義後其型別已經確定,不可再將其他型別的值賦給變數。
var name = "hi"; //String型別 name = 3; //此處編譯器會報錯,name被定義賦值之後已經是一個String型別,不可再賦值int型別值 複製程式碼
2.也可以使用特定型別來定義變數
String name = "bruce"; //String型別 int age = 18; //int型別 複製程式碼
3.如果變數不限於單個型別,則可以使用dynamic或Object來定義變數
dynamic value = 18; print("value = $value"); value = "bruce"; print("value = $value"); value = 3.5; print("value = $value"); Object val = 18; print("val = $val"); val = "bruce"; print("val = $val"); val = 3.5; print("val = $val"); 複製程式碼
輸出結果為
value = 18 value = bruce value = 3.5 val = 18 val = bruce val = 3.5 複製程式碼
變數的預設值
由於前文關於Dart的一些概念中說到過,能夠放在變數中的所有內容都是物件,所以如果一個變數沒有初始化值,那它的預設值就為null。
int value1; print("value1 = $value1"); bool value2; print("value2 = $value2"); var value3; print("value3 = $value3"); dynamic value4; print("value4 = $value4"); 複製程式碼
輸出結果為
value1 = null value2 = null value3 = null value4 = null 複製程式碼
final 和 const
如果不打算更改變數,可以使用final或者const。一個final變數只能被設定一次,而const變數是編譯時常量,定義時必須賦值。
// Person類 class Person { static const desc = "This is a Person class"; //必須定義時賦值,否則編譯時報錯 final name; Person(this.name); //物件初始化時賦值一次 } // 定義一個Person物件 Person p = Person("Bruce"); //建立物件時設定一次name print("p.name = ${p.name}"); //可正常輸出 p.name = Bruce p.name = "haha"; //編譯器報錯 複製程式碼
內建型別
Dart語言支援以下型別
-
numbers
包含int和double兩種型別,沒有像Java中的float型別,int和double都是num的子型別。
-
strings
Dart的字串是一系列UTF-16程式碼單元。建立方法如下:
String str1 = "hello"; //可以使用單引號或雙引號 print("str1 = $str1"); String str2 = """Hi,Bruce This is Xiaoming. """; //使用帶有單引號或雙引號的三重引號可以建立多行字串 print("str2 = $str2"); 複製程式碼
輸出結果為
str1 = hello str2 = Hi,Bruce This is Xiaoming. 複製程式碼
-
booleans
Dart有一個名為bool的型別,只有兩個物件具有bool型別:true和false,他們都是編譯時常量。
-
lists
和其他程式語言常見的集合一樣,Dart中使用的集合是陣列或有序的物件組。Dart中陣列是List物件。
List arr = ["Bruce", "Nick", "John"]; print("arr = $arr"); 複製程式碼
- maps
Map map = { "name": "Bruce", "age": 18, "high": 1.70 }; print("map = $map"); print("map['name'] = ${map['name']}"); var map1 = { 1: "hi", 2: "hello", 3: "yep" }; print("map1 = $map1"); print("map1[1] = ${map1[1]}"); 複製程式碼
輸出結果為
map = {name: Bruce, age: 18, high: 1.7} map['name'] = Bruce map1 = {1: hi, 2: hello, 3: yep} map1[1] = hi 複製程式碼
-
runes
符文是字串的UTF-32程式碼點。在字串中表示32位Unicode值需要特殊語法,常用方法是 \uXXXX,其中XXXX是4位十六進位制值,比如小心心(♥)是
\u2665
。要指定多於或少於4個十六進位制數字,請將值放在大括號中。 比如,微笑(:laughing:)是\u{1f600}
。
String smile = '\u{1f600}'; print("微笑:$smile"); Runes input = new Runes( '\u2665\u{1f605}\u{1f60e}\u{1f47b}\u{1f596}\u{1f44d}'); print(String.fromCharCodes(input)); 複製程式碼
輸出結果為
微笑::grinning: ♥:sweat_smile::sunglasses::ghost::vulcan_salute::+1: 複製程式碼
函式
Dart是一種真正的面嚮物件語言,因此即使是函式也是物件並且具有型別Function。這意味著函式可以分配給變數或作為引數傳遞給其他函式。
定義方法
和絕大多數程式語言一樣,Dart函式通常的定義方式為
String getName() { return "Bruce"; } 複製程式碼
如果函式體中只包含一個表示式,則可以使用簡寫語法
String getName() => "Bruce"; 複製程式碼
可選引數
Dart函式可以設定可選引數,可以使用命名引數也可以使用位置引數。
命名引數,定義格式如 {param1, param2, …}
// 函式定義 void showDesc({var name, var age}) { if(name != null) { print("name = $name"); } if(age != null) { print("age = $age"); } } // 函式呼叫 showDesc(name: "Bruce"); // 輸出結果 name = Bruce 複製程式碼
位置引數,使用 [] 來標記可選引數。
// 函式定義 void showDesc(var name, [var age]) { print("name = $name"); if(age != null) { print("age = $age"); } } // 函式呼叫 showDesc("Bruce"); // 輸出結果 name = Bruce 複製程式碼
預設值
函式的可選引數也可以使用 = 設定預設值
// 函式定義 void showDesc(var name, [var age = 18]) { print("name = $name"); if(age != null) { print("age = $age"); } } // 函式呼叫 showDesc("Bruce"); // 輸出結果 name = Bruce age = 18 複製程式碼
main函式
和其他程式語言一樣,Dart中每個應用程式都必須有一個頂級main()函式,該函式作為應用程式的入口點。
函式作為引數
Dart中的函式可以作為另一個函式的引數。
// 函式定義 void println(String name) { print("name = $name"); } void showDesc(var name, Function log) { log(name); } // 函式呼叫 showDesc("Bruce", println); // 輸出結果 name = Bruce 複製程式碼
匿名函式
// 函式定義 void showDesc(var name, Function log) { log(name); } // 函式呼叫,匿名函式作為引數 showDesc("Bruce", (name) { print("name = $name"); }); // 輸出結果 name = Bruce 複製程式碼
巢狀函式
Dart支援巢狀函式,也就是函式中可以定義函式。
// 函式定義 void showDesc(var name) { print("That is a nested function!"); //函式中定義函式 void println(var name) { print("name = $name"); } println(name); } // 函式呼叫 showDesc("Bruce"); // 輸出結果 That is a nested function! name = Bruce 複製程式碼
運算子
Dart中使用到的運算子如下表格
Description | Operator |
---|---|
一元后綴 | expr++ expr-- () [] . ?. |
一元字首 | -expr !expr ~expr ++expr --expr |
乘除操作 | * / % ~/ |
加減操作 | + - |
移位 | << >> |
按位與 | & |
按位異或 | ^ |
按位或 | | |
比較關係和型別判斷 | >= > <= < as is is! |
等判斷 | == != |
邏輯與 | && |
邏輯或 | || |
是否null | ?? |
條件語句操作 | expr1 ? expr2 : expr3 |
級聯操作 | .. |
分配賦值操作 | = *= /= ~/= %= += -= <<= >>= &= ^= |= ??= |
下面就對一些對於Java或Objective-C來說未使用過的運算子通過程式碼來做個介紹。
-
?.
的使用
//定義類 class Person { var name; Person(this.name); } // 呼叫 Person p; var name = p?.name; //先判斷p是否為null,如果是,則name為null;如果否,則返回p.name值 print("name = $name"); // 輸出結果 name = null 複製程式碼
-
~/
的使用
// 程式碼語句 var num = 10; var result = num ~/ 3; //得出一個小於等於(num/3)的最大整數 print("result = $result"); // 輸出結果 result = 3 複製程式碼
-
as
的使用,as用來做型別轉化
// 類定義 class Banana { var weight; Banana(this.weight); } class Apple { var weight; Apple(this.weight); } // 呼叫 dynamic b = Banana(20); (b as Banana).weight = 20; // 正常執行 print("b.weight = ${(b as Banana).weight}"); (b as Apple).weight = 30; // 型別轉換錯誤,執行報錯 print("b.weight = ${(b asApple).weight}"); //輸出結果 b.weight = 20 Uncaught exception: CastError: Instance of 'Banana': type 'Banana' is not a subtype of type 'Apple' 複製程式碼
-
is
的使用
// 函式和類程式碼定義 getFruit() => Banana(20); // 獲取一個水果物件 class Banana { var weight; Banana(this.weight); } class Apple { var color; Apple(this.color); } // 呼叫 var b = getFruit(); if(b is Apple) { //判斷物件是否為Apple類 print("The fruit is an apple"); } else if(b is Banana) { //判斷水果是否為Banana類 print("The fruit is a banana"); } // 輸出結果 The fruit is a banana 複製程式碼
-
??
的使用
// 操作程式碼塊 String name; String nickName = name ?? "Nick"; //如果name不為null,則nickName值為name的值,否則值為Nick print("nickName = $nickName"); name = "Bruce"; nickName = name ?? "Nick"; //如果name不為null,則nickName值為name的值,否則值為Nick print("nickName = $nickName"); // 輸出結果 nickName = Nick nickName = Bruce 複製程式碼
-
..
的使用,級聯操作允許對同一個物件進行一系列操作。
// 類定義 class Banana { var weight; var color; Banana(this.weight, this.color); void showWeight() { print("weight = $weight"); } void showColor() { print("color = $color"); } } // 呼叫 Banana(20, 'yellow') ..showWeight() ..showColor(); // 輸出結果 weight = 20 color = yellow 複製程式碼
控制流語句
Dart中的控制流語句和其他語言一樣,包含以下方式
- if and else
- for迴圈
- while和do-while迴圈
- break和continue
- switch-case語句
以上控制流語句和其他程式語言用法一樣,switch-case有一個特殊的用法如下,可以使用continue語句和標籤來執行指定case語句。
var fruit = 'apple'; switch (fruit) { case 'banana': print("this is a banana"); continue anotherFruit; anotherFruit: case 'apple': print("this is an apple"); break; } // 輸出結果 this is an apple 複製程式碼
異常
Dart的異常捕獲也是使用try-catch語法,不過與java等語言稍有不同
// 定義一個丟擲異常的函式 void handleOperator() => throw Exception("this operator exception!"); // 函式呼叫 try { handleOperator(); } on Exception catch(e) { print(e); } finally { // finally語句可選 print("finally"); } // 輸出結果 Exception: this operator exception! finally 複製程式碼
類
Dart是一種面向物件的語言,具有類和基於mixin的繼承。同Java一樣,Dart的所有類也都繼承自Object。
建構函式
Dart的建構函式同普通函式一樣,可以定義無參和有參,命名引數和位置引數,可選引數和給可選引數設定預設值等。Dart的建構函式有以下幾個特點:
- 可以定義命名建構函式
- 可以在函式體執行之前初始化例項變數
- 子類不從父類繼承建構函式,定義沒有建構函式的子類只有無參無名稱的建構函式
- 子類定義建構函式時預設繼承父類無參建構函式,也可繼承指定有引數的建構函式;
命名建構函式和函式體執行前初始化例項變數
// 類定義 class Tree { var desc; // 命名建構函式 Tree.init() { desc = "this is a seed"; } // 函式體執行之前初始化例項變數 Tree(var des) : desc = des; } // 建構函式呼叫 Tree t = Tree.init(); print("${t.desc}"); Tree t1 = Tree("this is a tree"); print("${t1.desc}"); // 輸出結果 this is a seed this is a tree 複製程式碼
建構函式繼承
// 類定義 class Fruit { Fruit() { print("this is Fruit constructor with no param"); } Fruit.desc(var desc) { print("$desc in Fruit"); } } class Apple extends Fruit { Apple():super() { print("this is Apple constructor with no param"); } // 預設繼承無參建構函式 Apple.desc(var desc) { print('$desc in Apple'); } } // 建構函式呼叫 Apple(); Apple.desc("say hello"); // 輸出結果 this is Fruit constructor with no param this is Apple constructor with no param this is Fruit constructor with no param say hello in Apple 複製程式碼
mixin繼承
mixin是一種在多個類層次結構中重用類程式碼的方法。
// 類定義 class LogUtil { void log() { print("this is a log"); } } class Fruit { Fruit() { print("this is Fruit constructor with no param"); } } class Apple extends Fruit with LogUtil { Apple():super() { print("this is Apple constructor with no param"); } } // 呼叫 Apple a = Apple(); a.log(); //可執行從LogUtil繼承過來的方法 // 輸出結果 this is Fruit constructor with no param this is Apple constructor with no param this is a log 複製程式碼
泛型
Dart同Java一樣,也支援泛型。
// 類定義 class Apple { var desc; Apple(this.desc); void log() { print("${this.desc}"); } } class Banana { var desc; Banana(this.desc); void log() { print("${this.desc}"); } } class FruitFactory<T> { T produceFruit(T t) { return t; } } // 呼叫 FruitFactory<Banana> f = FruitFactory<Banana>(); Banana b = f.produceFruit(Banana("a banana")); b.log(); FruitFactory<Apple> f1 = FruitFactory<Apple>(); Apple a = f1.produceFruit(Apple("an apple")); a.log(); // 輸出結果 a banana an apple 複製程式碼
寫在最後
本文主要針對Dart不同於其他程式語言的一些語法特性進行了分析和舉例,相信讀過文字之後大家會對Dart語法有個很系統的瞭解,後邊我們就可以開啟Flutter應用開發之旅了。