第3章
Dart語言簡述
在前兩章介紹Flutter基礎知識時,多多少少使用了一些Dart語言。作為Flutter SDK指定的語言,我們很有必要補充一下Dart語言的基礎知識,包括它的文法特性、基本語句、面向對象等知識點。
在這一章裡将講解Dart語言的知識點:
□Dart重要概念與常用開發庫
□變量與基本資料類型
□函數
□運算符
□流程控制語句
□異常處理
□面向對象
□泛型
□庫的使用
□異步支援
□中繼資料
□注釋
3.1 Dart重要概念與常用開發庫
Dart誕生于2011年10月10日,谷歌Dart語言項目的上司人Lars Bak在丹麥舉行的Goto會議上宣布,Dart是一種“結構化的Web程式設計”語言,Dart程式設計語言在所有現代浏覽器和環境中提供高性能。
Dart雖然是谷歌開發的計算機程式設計語言,但後來被ECMA認定為标準。這門語言用于Web、伺服器、移動應用和物聯網等領域的開發,是寬松開源許可證(修改的BSD證書)下的開源軟體。
Dart最新的版本是Dart2,Dart 2是一款高效、簡潔、已認證實戰檢驗的語言,能夠應對現代應用程式開發的挑戰。Dart 2大大加強和精簡了類型系統,清理了文法,并重建了大部分開發工具鍊,使移動和Web開發變得更加愉快和高效。Dart 2還融合了包括Flutter、AdWords和AdSense等工具開發者對該語言早期使用的經驗教訓,以及針對客戶回報的成千上萬大大小小的問題進行了改進。
那麼Flutter和Dart有什麼關系?确實有關系。早期的Flutter團隊評估了十多種語言才選擇了Dart,因為它符合建構使用者界面的方式。以下是Flutter團隊看重Dart語言的部分特性:
□Dart是AOT(Ahead Of Time)編譯的,編譯成快速、可預測的本地代碼,使Flutter幾乎都可以使用Dart編寫。這不僅使Flutter變得更快,而且幾乎所有的元件(包括所有的小部件)都可以定制。
□Dart也可以JIT(Just In Time)編譯,開發周期異常快,工作流颠覆正常(包括Flutter流行的亞秒級有狀态熱重載)。
□Dart可以更輕松地建立以60fps運作的流暢動畫和轉場。Dart可以在沒有鎖的情況下進行對象配置設定和垃圾回收。就像JavaScript一樣,Dart避免了搶占式排程和共享記憶體(因而也不需要鎖)。由于Flutter應用程式被編譯為本地代碼,是以不需要在領域之間建立緩慢的橋梁(例如,JavaScript到本地代碼)。它的啟動速度也快得多。
□Dart使Flutter不需要單獨的聲明式布局語言(如JSX或XML),或單獨的可視化界面建構器,因為Dart的聲明式程式設計布局易于閱讀和可視化。所有的布局使用一種語言,聚集在一處,Flutter很容易提供進階工具,使布局更簡單。
□開發人員發現Dart特别容易學習,因為它具有靜态和動态語言使用者都熟悉的特性。
并非所有這些功能都是Dart獨有的,但Dart将這些功能組合得恰到好處,使Dart在實作Flutter方面獨一無二。是以,沒有Dart,很難想象Flutter像現在這樣強大。
當你想建立移動App、Web App、Command-line應用時,都可以使用Dart語言,如圖3-1所示。
Dart重要的概念如下:
□所有的東西都是對象,無論是變量、數字、函數等都是對象。所有的對象都是類的執行個體。所有的對象都繼承自内置的Object類。這點類似于Java語言“一切皆為對象”。
□程式中指定資料類型使得程式合理地配置設定記憶體空間,并幫助編繹器進行文法檢查。但是,指定類型不是必須的。Dart 語言是弱資料類型。
□Dart代碼在運作前解析。指定資料類型和編譯時的常量,可以提高運作速度。

□Dart程式有統一的程式入口:main()。這一點與Java、C / C++語言相像。
□Dart沒有public、protected和private的概念。私有特性通過變量或函數加上下劃線來表示。
□Dart的工具可以檢查出警告資訊(warning)和錯誤資訊(errors)。警告資訊隻是表明代碼可能不工作,但是不會妨礙程式運作。錯誤資訊可以是編譯時的錯誤,也可能是運作時的錯誤。編譯時的錯誤将阻止程式運作,運作時的錯誤将會以異(exception)的方式呈現。
□Dart支援anync/await異步處理。
Dart語言常用庫如表3-1所示。
其中如下三個開發庫的使用頻率最高:
□dart:core:核心庫,包括strings、numbers、collections、errors、dates、URIs等。
□dart:html:網頁開發裡DOM相關的一些庫。
□dart:io:I/O指令行使用的I/O庫。
dart:core庫是Dart語言初始已經包含的庫,其他的任何庫在使用前都需要加上import 語句。例如,使用dart:html可以使用如下的指令:
import 'dart:html';
使用官方提供的pub工具可以安裝豐富的第三方庫。第三方庫的位址為:pub.dartlang.org。
3.2 變量與基本資料類型
在Dart裡,變量聲明使用var關鍵字,如下所示:
var name = '小張';
在Dart語言裡一切皆為對象,是以如果沒有将變量初始化,那麼它的預設值為null。下面的示例代碼判斷name是否為null:
int name;
if(name == null);
1.常量和固定值
常量及固定值在開發中很常見,比如星期一到星期天、一年12個月,這些資料都可以定義成常量形式。
□如果定義的變量不會變化,可以使用final或const來指明。const是一個編譯時的常量,final的值隻能被設定一次,示例如下:
final username = '張三'; // 定義了一個常量
// username = '李四'; // 會引發一個錯誤
第一行代碼設定了一個常量,如果第二行進新重新指派,那麼将引發異常。
□通過對const類型做四則運算将自動得到一個const類型的值。下面的代碼會得到一個常量,計算圓的面積:
const pi = 3.1415926;
const area = pi 100 100;
□可以通過const來建立常量的值,就是說const[] 本身是構造函數,示例代碼如下所示:
final stars = const [];
const buttons = const [];
2.基本資料類型
Dart語言常用的基本資料類型包括:Number、String、Boolean、List、Map。
(1)Number類型
Number類型包括如下兩類:
□int整形。取值範圍:-2^53 到 2^53。
□doble浮點型。64 位長度的浮點型資料,即雙精度浮點型。
int和double類型都是 num 類型的子類。int類型不能包含小數點。num類型包括的操作有:+,-,*,/以及位移操作>>。num類型包括的常用方法有:abs、ceil和floor。
(2)String類型
String類型也就是字元串類型,在開發中大量使用。定義的例子如下所示:
var s1 = 'hello world'; //單雙引号都可以
String類型可以使用 + 操作,非常友善,具體用法示例如下所示:
var s1 = 'hi ';
var s2 = 'flutter';
var s3 = s1 + s2;
print(s3);
上面代碼列印輸出”hi flutter“字元串。
可以使用三個單引号或雙引号來定義多行的String類型,在Flutter中我們專門用來表示大文本塊。示例代碼如下所示:
var s1 = '''
請注意這是一個用三個單引号包裹起來的字元串,
可以用來添加多行資料。
''';
var s2 = """同樣這也是一個用多行資料,
隻不過是用雙引号包裹起來的。
""";
(3)Boolean類型
Dart是強bool類型檢查,隻有bool 類型的值是true才被認為是true。有的語言裡0是false,大于0是true。在Dart語言裡則不是,值必須為true或者false。下面的示例代碼編譯不能正常通過,原因是sex變量是一個字元串,不能使用條件判斷語句,必需使用bool類型才可以:
var sex = '男';
if (sex) {
print('你的性别是!' + sex);
}
(4)List類型
在Dart語言中,具有一系列相同類型的資料稱為List 對象。Dart裡的List對象類似于JavaScript語言的數組Array對象。定義List的例子如下所示:
var list = [1, 2, 3];
List對象的第一個元素的索引是0,最後一個元素的索引是list.lenght – 1,代碼如下所示:
var list = [1,2,3,4,5,6];
print(list.length);
print(list[list.length - 1]);
上面的代碼輸出長度為6,最後一個元素值也為6。
(5)Map類型
Map類型将key和value關聯在一起,也就是健值對。像其他支援Map的程式設計語言一樣,key必須是唯一的。
如下代碼是Map對象的定義,示例定義了一個關于星期的鍵值對對象:
var week = {
'Monday' : '星期一',
'Tuesday': '星期二',
'Wednesday' : '星期三',
'Thursday': '星期四',
'Friday': '星期五',
'Saturday': '星期六',
'Sunday': '星期日',
};
也可以使用Map對象的構造函數Map()來建立Map對象,如下所示:
var week = new Map();
week['Monday'] = '星期一';
week['Tuesday'] = '星期二';
week['Wednesday'] = '星期三';
week['Thursday'] = '星期四';
week['Friday'] = '星期五';
week['Saturday'] = '星期六';
week['Sunday'] = '星期日';
添加新的key-value對,再給week添加一個值,注意,其中0為鍵不是數組的下标索引:
week['0'] = '星期一';
檢查key是否在Map對象中:
assert(week['Monday'] == null);
使用length來擷取key-value對的數量,現在我們調用length輸出長度結果為8,原因是後面又添加了一個資料,代碼如下所示:
print(week.length);
3.3 函數
Dart是一個面向對象的語言,是以函數也是對象,函數屬于Function對象。
函數可以像參數一樣傳遞給其他函數,這樣便于做回調處理。
如下示例為判斷兩個字元串是否相等:
//判斷兩個字元串是否相等
bool equal(String str1, String str2) {
return str1 == str2;
1.可選參數
将參數使用中括号[]括起來,用來表明是可選位置參數。例如,總共傳入了三個參數,其中name和sex是必需傳入的參數,from參數可以不傳,代碼如下:
//擷取使用者資訊
String getUserInfo(String name, String sex, [String from]) {
var info = '$name的性别是$sex';
if (from != null) {
info = '$info來自$from';
return info;
void test(){
print(getUserInfo('小王', '男'));
調用上面的test方法可以輸出“小王的性别是男”,但是不會輸出來自哪裡。
2.參數預設值
如果參數指定了預設值,當不傳入值時,函數裡會使用這個預設值。如果傳入了值,則用傳入的值取代預設值。通常參數的預設值為null。改造上面擷取使用者資訊的例子,給from參數賦上預設值,具體代碼如下:
//擷取使用者資訊 使用等号(
=
)來設定默位置字參數
String getUserInfo(String name, String sex, [String from = '中國']) {
info = '$info來自$from';
}
調用上面的test方法可以輸出“小王的性别是男來自中國”,這裡大家會發現輸出了來自哪裡,就是因為我們使用了預設參數值。
3. main函數
Flutter應用程式必須要有一個main函數,和其他語言一樣作為程式的入口函數。下面的代碼表示應用要啟動MyApp類:
void main() => runApp(MyApp());
4.函數傳回值
在Dart語言中,函數的傳回值有如下特點:
□所有的函數都會有傳回值。
□如果沒有指定函數傳回值,則預設的傳回值是null。
□沒有傳回值的函數,系統會在最後添加隐式的return語句。
3.4 運算符
Dart 支援各種類型的運算符,并且其中的一些操作符還能進行重載。完整的操作符如表3-2所示。
使用運算符時可以建立表達式,以下是運算符表達式的一些示例:
a++
a--
a + b
a = b
a == b
expr ? a : b
a is T
在表3-2操作符表中,操作符的優先級由上到下逐個減小,上面行内的操作符優先級大于下面行内的操作符。例如,“乘法類型”操作符%的優先級比“等價”操作符==要高,而==操作符的優先級又比“邏輯與”操作符&&要高。注意使用運算符時的順序,方法如下所示:
// 1.使用括号來提高可讀性
if ((n % i == 0) && (d % i == 0))
// 2.難以閱讀,但是和上面等價
if (n % i == 0 && d % i == 0)
提示:對于二進制運算符,其左邊的操作數将會決定使用的操作符的種類。例如,當你使用一個Vector對象以及一個Point對象時,aVector + aPoint使用的 + 是由Vector 所定義的。
1.算術運算符
Dart支援常用的算術運算符如下所示:
Dart還支援字首和字尾遞增和遞減運算符,如下所示:
2.關系運算符
等式和關系運算符的含義如下:
有時需要判斷兩個對象是否相等,請使用= =運算符。
下面是使用每個等式和關系運算符的示例:
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
3.類型測試操作符
as、is和is! 操作符在運作時用于檢查類型非常友善,含義如下所示:
如果obj實作了T所定義的接口,那麼obj is T将傳回 true。
使用as操作符可以把一個對象轉換為指定類型,前提是能夠轉換。轉換之前用is判斷一下更保險。如下面這段代碼:
if (user is User) {
//類型檢測
user.name = 'Flutter';
如果能确定user是User的執行個體,則你可以通過as直接簡化代碼:
(user as User).name = 'Flutter';
注意: 上面兩段代碼并不相等。如果user的值為null或者不是一個User對象,第一段代碼不會做任何事情,第二段代碼會報錯。
4.指派操作符
可以使用=運算符指派。要僅在變量為null時指派,請使用??=運算符。如下面代碼所示:
// 指派給a
a = value;
// 如果b為空,則将值配置設定給b;否則,b保持不變
b ??= value;
諸如+=之類的複合指派運算符将操作與指派相結合。以下是複合指派運算符的工作方式:
5.邏輯運算符
可以使用邏輯運算符反轉或組合布爾表達式,邏輯運算符如下所示:
6.位運算符
通常我們指位運算為 << 或 >> 移動位運算,通過操作位的移動來達到運算的目的,而 &、 |、 ^、 ~expr也是操作位來達到運算的目的。具體含義如下所示:
7.條件表達式
Dart有兩個運算符,可用來簡明地評估可能需要if-else語句的表達式。如下代碼即為一種條件表達式,也可以稱為三元表達式。如果條件為真,傳回expr1,否則傳回expr2:
condition ? expr1 : expr2
第二種如下所示,如果expr1為非空,則傳回其值;否則,計算并傳回expr2的值:
expr1 ?? expr2
8.級聯操作
級聯操作用兩個點(..)表示,可對同一對象執行一系列操作。類似于Java語言裡點點點處理或JavaScript裡的Promise的then處理。級聯操作主要的目的是為了簡化代碼,示例如下:
querySelector('#btnOK) // 擷取一個id為btnOK的按鈕對象
..text = '确定' // 使用它的成員
..classes.add('ButtonOKStyle')
..onClick.listen((e) => window.alert('确定'));
第一個方法調用querySelector,傳回一個按鈕對象,然後再設定它的文本為“确定”,再給這個按鈕添加一個樣式叫'ButtonOKStyle',最後再監聽單擊事件,事件彈出一個顯示“确定”的Alert。這個例子相當于如下操作:
var button = querySelector('#btnOK);
button.text = '确定';
button.classes.add(''ButtonOKStyle'');
button.onClick.listen((e) => window.alert('确定'));
注意 嚴格來說,級聯的“雙點”符号不是運算符,這隻是Dart文法的一部分。
3.5 流程控制語句
Dart語言的流程控制語句如下:
□if和else
□for(循環)
□while和do-while(循環)
□break和continue
□switch和case
□assert(斷言)
□try-catch和throw
1. if和else
Dart支援if及else的多種組合,示例代碼如下:
String today = 'Monday';
if (today == 'Monday') {
print('今天是星期一');
} else if (today == 'Tuesday') {
print('今天是星期二');
} else {
print('今天是個好日子');
上面的代碼輸出“今天是星期一”,條件語句走到第一條判斷就停止了。
2. for
下面舉例說明for循環,首先定義了一個字元串“Hello Dart”,然後使用for循環向message 變量裡寫入5個同樣的字元“!”,如下所示:
var message = new StringBuffer("Hello Dart");
for (var i = 0; i < 5; i++) {
message.write('!');
print(message);
上面的代碼會輸出“Hello Dart!!!!!”,注意值是字元串向尾部添加的。除了正常的for 循環外,針對可以序列化的操作數,可以使用forEach() 方法。當不關心操作數的目前下标時,forEach()方法是很簡便的。如下代碼所示:
var arr = [0, 1, 2, 3, 4, 5, 6];
for (var v in arr) {
print(v);
}
上面的代碼會按序列輸出 0 1 2 3 4 5 6。
3. while和do-while
下面舉例說明while循環,其中定義了一個變量temp,temp在循環體内自動加1,當條件(temp<5)不滿足時會退出循環,如下所示:
var _temp = 0;
while(temp < 5){
print("這是一個while循環: " + (_temp).toString());
_temp ++;
接下來我們看一下例子用do-while循環,代碼如下所示:
do{
print("這是一個循環: " + (_temp).toString());
_temp ++;
}
while(_temp < 5);
上面的兩個例子都對應如下輸出:
flutter: 這是一個循環: 0
flutter: 這是一個循環: 1
flutter: 這是一個循環: 2
flutter: 這是一個循環: 3
flutter: 這是一個循環: 4
4. break和continue
break用來跳出循環,改造前面的循環例子,代碼如下:
for (var v in arr) {
if(v == 2 ){
break;
}
print(v);
}
上面的代碼當v等于2時循環結束。是以程式輸出0,1。現在我們把break改為continue,代碼如下所示:
for (var v in arr) {
if(v == 2 ){
//break;
continue;
}
print(v);
}
改為continue後,當v等于2時循環隻是跳出本次循環,代碼還會繼續向下執行,是以輸出的結果是0,1,3,4,5,6。
5. switch和case
Dart中switch / case 語句使用 == 操作來比較整數、字元串或其他編譯過程中的常量,進而實作分支的作用。switch / case 語句的前後操作數必須是相同類型的對象執行個體。每一個非空的case子句最後都必須跟上break語句。示例如下所示:
switch (today) {
case 'Monday':
print('星期一');
break;
case 'Tuesday':
print('星期二');
break;
}
上面這段代碼也可以用if /else 語句,輸出相同的結果,代碼輸出為“星期一”。
6. assert
Dart語言通過使用assert 語句來中斷正常的執行流程,當assert 判斷的條件為false時發生中斷。assert 判斷的條件是任何可以轉化為boolean類型的對象,即使是函數也可以。如果assert的判斷為true,則繼續執行下面的語句;反之則會抛出一個斷言錯誤異常AssertionError。代碼如下所示:
//确定變量的值不為null
assert(text != null);
3.6 異常處理
異常是表示發生了意外的錯誤,如果沒有捕獲異常,引發異常的隔離程式将被挂起,并且程式将被終止。
Dart代碼可以抛出并捕獲異常,但與Java相反,Dart的所有異常都是未檢查的異常。方法不聲明它們可能抛出哪些異常,也不需要捕獲任何異常。
Dart提供了異常和錯誤類型以及許多預定義的子類型。當然,也可以定義自己的異常。然而,Dart程式可以抛出任何非空對象。
1.抛出異常
下面是一個抛出或引發異常的例子:
throw FormatException('抛出一個FormatException異常');
你也可以抛出任意對象:
throw '資料非法!';
提示: 穩定健壯的程式一定是做了大量異常處理的,是以建議你在編寫程式時盡量考慮到可能發生異常的情況。
2.捕獲異常
你可以指定一個或兩個參數來捕獲異常(catch),第一個是抛出的異常,第二個是堆棧跟蹤(StackTrace對象)。如下面代碼所示:
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
上面的代碼第一個catch用來捕獲異常詳細資訊,第二個catch是堆棧跟蹤資訊。
3. Finally
要確定某些代碼能夠運作,無論是否抛出異常,請使用finally子句。如果沒有catch子句比對異常,則異常在finally子句運作後傳播。如下面代碼所示,在最下加上了finally語句:
// ...
} on Exception catch (e) {
print('Exception details:n $e');
} catch (e, s) {
print('Stack trace:n $s');
} finally {
print('Do some thing:n');
3.7 面向對象
Dart作為進階語言支援面向對象的很多特性,并且支援基于mixin的繼承方式。基于mixin的繼承方式是指:一個類可以繼承自多個父類,相當于其他語言裡的多繼承。所有的類都有同一個基類Object,這個特性類似于Java語言,Java所有的類也都是繼承自Object,也就是說一切皆為對象。
使用new語句執行個體化一個類,如下所示:
//執行個體化了一個User類的對象user
var user = new User('張三', 20);
3.7.1 執行個體化成員變量
定義一個User類,在裡類裡添加兩個成員變量name與age,代碼如下所示:
class User{
String name; //name成員變量
int age; //age成員變量
類定義中所有的變量都會隐式的定義 setter 方法,針對非空的變量會額外增加 getter 方法。執行個體化成員變量請參考如下代碼:
main() {
var user = new User();
user.name = '張三';//相當于使用了name的setter方法
user.age = 20;
3.7.2 構造函數
1.正常構造函數
構造函數是用來構造目前類的函數,是一種特殊的函數,函數名稱必須要和類名相同才行。如下代碼為User類添加了一個構造函數,函數裡給User類的兩個成員變量初始化了值:
String name;
int age;
User(String name,int age){
this.name = name;
this.age = age;
this關鍵字指向了目前類的執行個體。上面的代碼可以簡化為:
User(this.name,this.age);
2.命名的構造函數
使用命名構造函數從另一類或現有的資料中快速實作構造函數,代碼如下所示:
class User {
String name;
User(this.name, this.age);
//命名的構造函數
User.fromJson(Map json) {
name = json['name'];
age = json['age'];
3.構造函數初始化清單
除了調用父類的構造函數,也可以通過初始化清單在子類的構造函數運作前來初始化執行個體的成員變量值,代碼如下所示:
final String name;
final int age;
User(name, age)
: name = name,
age = age;
var p = new User('張三', 20);
3.7.3 讀取和寫入對象
get()和set()方法是專門用于讀取和寫入對象的屬性的方法,每一個類的執行個體,系統都隐式地包含有get()和set() 方法。這和很多語言裡的VO類相似。
例如,定義一個矩形的類,有上、下、左、右四個成員變量:top、bottom、left、right,使用get及set關鍵字分别對right及bottom進行擷取和設定值。代碼如下所示:
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
//擷取right值
num get right => left + width;
//設定right值 同時left也發生變化
set right(num value) => left = value - width;
//擷取bottom值
num get bottom => top + height;
//設定bottom值 同時top也發生變化
set bottom(num value) => top = value - height;
var rect = new Rectangle(3, 4, 20, 15);
print('left:'+rect.left.toString());
print('right:'+rect.right.toString());
rect.right = 30;
print('更改right值為30');
print('left:'+rect.left.toString());
print('top:'+rect.top.toString());
print('bottom:'+rect.bottom.toString());
rect.bottom = 50;
print('更改bottom值為50');
print('top:'+rect.top.toString());
上面例子對應的輸出為:
flutter: left:3
flutter: right:23
flutter: 更改right值為30
flutter: left:10
flutter: right:30
flutter: top:4
flutter: bottom:19
flutter: 更改bottom值為50
flutter: top:35
flutter: bottom:50
3.7.4 重載操作
編寫一個例子,定義一個Vector向量類,編寫兩個方法分别用于重載加号及減号,那麼當兩個向量相加,就表示它們的x值及y值相加,當兩個向量相減,就表示它們的x值及y值相減。完整的示例代碼如下:
//定義一個向量類
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
//重載加号 + (a + b).
Vector operator +(Vector v) {
return new Vector(x + v.x, y + v.y);
//重載減号 - (a - b).
Vector operator -(Vector v) {
return new Vector(x - v.x, y - v.y);
//執行個體化兩個向量
final v = new Vector(2, 3);
final w = new Vector(2, 2);
final r1 = v + w;
print('r1.x='+r1.x.toString() + ' r1.y=' + r1.y.toString());
final r2 = v - w;
print('r2.x='+r2.x.toString() + ' r2.y=' + r2.y.toString());
上面代碼的輸出結果為:
flutter: r1.x=4 r1.y=5
flutter: r2.x=0 r2.y=1
3.7.5 繼承類
繼承是面向對象程式設計技術的一塊基石,因為它允許建立分等級層次的類。繼承就是子類繼承父類的特征和行為,使得子類對象(執行個體)具有父類的執行個體域和方法;或子類從父類繼承方法,使得子類具有父類相同的行為。Dart裡使用extends 關鍵字來建立一個子類,super關鍵子來指定父類。
接下來定義一個動物類,動物具有吃和跑兩種能力。再定義一個人類,人類是屬于動物類的,人類不僅會吃和會跑,人類還會說、會學習。是以人類相當于動物類的一個擴充。完整的示例如下所示:
//動物類
class Animal {
//動物會吃
void eat(){
print('動物會吃');
//動物會跑
void run(){
print('動物會跑');
//人類
class Human extends Animal {
//人類會說
void say(){
print('人類會說');
//人類會學習
void study(){
print('人類會學習');
print('執行個體化一個動物類');
var animal = new Animal();
animal.eat();
animal.run();
print('執行個體化一個人類');
var human = new Human();
human.eat();
human.run();
human.say();
human.study();
上面的例子輸出結果如下:
flutter: 執行個體化一個動物類
flutter: 動物會吃
flutter: 動物會跑
flutter: 執行個體化一個人類
flutter: 人類會說
flutter: 人類會學習
3.7.6 抽象類
抽象類類似于Java語言中的接口。抽象類裡不具體實作方法,隻是寫好定義接口,具體實作留着調用的人去實作。抽象類可以使用abstract關鍵字定義類。
接下來寫一個資料庫操作的抽象類的例子。定義一個抽象類叫DateBaseOperate,裡面定義4個資料庫常用的操作方法“增删改查”。再定義一個類命名為DateBaseOperateImpl繼承自DateBaseOperate用來實作抽象類裡的方法。完整的代碼如下所示:
//資料庫操作抽象類
abstract class DateBaseOperate {
void insert(); //定義插入的方法
void delete(); //定義删除的方法
void update(); //定義更新的方法
void query(); //定義一個查詢的方法
//資料庫操作實作類
class DateBaseOperateImpl extends DateBaseOperate {
//實作了插入的方法
void insert(){
print('實作了插入的方法');
//實作了删除的方法
void delete(){
print('實作了删除的方法');
//實作了更新的方法
void update(){
print('實作了更新的方法');
//實作了一個查詢的方法
void query(){
print('實作了一個查詢的方法');
var db = new DateBaseOperateImpl();
db.insert();
db.delete();
db.update();
db.query();
上述代碼輸出結果為:
flutter: 實作了插入的方法
flutter: 實作了删除的方法
flutter: 實作了更新的方法
flutter: 實作了一個查詢的方法
3.7.7 枚舉類型
枚舉類型是一種特殊的類,通常用來表示相同類型的一組常量值。每個枚舉類型都用于一個index的getter,用來标記元素的元素位置。第一個枚舉元素的索引是0:
enum Color {
red,
green,
blue
擷取枚舉類中所有的值,使用value常數:
List colors = Color.values;
因為枚舉類裡面的每個元素都是相同類型,可以使用switch 語句來針對不同的值做不同的處理,示例代碼如下:
//定義一個顔色變量 預設值為藍色
Color aColor = Color.blue;
switch (aColor) {
case Color.red:
print('紅色');
break;
case Color.green:
print('綠色');
break;
default: //預設顔色
print(aColor); // 'Color.blue'
3.7.8 Mixins
Mixins(混入功能)相當于多繼承,也就是說可以繼承多個類。使用with關鍵字來實作Mixins的功能,示例代碼如下所示:
class S {
a() {print("S.a");}
class A {
a(){print("A.a");}
b(){print("A.b");}
class T = A with S;
main(List args) {
T t = new T();
t.a();
t.b();
上面代碼的輸出内容如下所示,從結果上來看T具有了S及A兩個類的方法:
S.a
A.b
3.8 泛型
泛型通常是為了類型安全而設計的,适當地指定泛型類型會生成更好的代碼,可以使用泛型來減少代碼重複。Dart中使用 的方式來定義泛型。例如,如果想要List隻包含字元串,可以将其聲明為list 。如下所示:
var names = new List();
names.addAll(['張三', '李四', '王五']);
1.用于集合類型
泛型用于List和Map類型參數化:
List:
Map:
例子代碼如下:
var names = ['張三', '李四', '王五'];
var weeks = {
'Monday' : '星期一',
'Tuesday': '星期二',
'Wednesday' : '星期三',
'Thursday': '星期四',
'Friday': '星期五',
'Saturday': '星期六',
'Sunday': '星期日',
};
2.在構造函數中參數化
Map 類型的例子如下:
var users = new Map();
3.9 庫的使用
1.引用庫
通過import 語句在一個庫中引用另一個庫的檔案。需要注意以下事項:
□在import語句後面需要接上庫檔案的路徑。
□對Dart 語言提供的庫檔案使用dart:xx 格式。
□第三方的庫檔案使用package:xx格式。
import的例子如下:
import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';
2.指定一個庫的字首
當引用的庫擁有互相沖突的名字,可以為其中一個或幾個指定不一樣的字首。這與命名空間的概念比較接近,示例代碼如下:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element(); // 使用lib1中的Element
lib2.Element element2 = new lib2.Element(); // 使用lib2中的Element
lib1/lib1.dart及lib2/lib2.dart裡都有Element類,如果直接引用就不知道具體引用哪個Element類,是以代碼中把lib2/lib2.dart指定成lib2,這樣使用lib2.Element就不會發生沖突。
3.引用庫的一部分
如果隻需要使用庫的一部分内容,可以有選擇地引用,有如下關鍵字:
□show 關鍵字:隻引用一點。
□hide 關鍵字:除此之外都引用。
示例代碼如下:
// 導入foo
import 'package:lib1/lib1.dart' show foo;
// 除了foo導入其他所有内容
import 'package:lib2/lib2.dart' hide foo;
代碼中的第一行隻引用lib1.dart下的foo部分,第二行代碼引用lib2.dart下的所有内容除了foo。
3.10 異步支援
Dart 語言是目前少數幾個支援異步操作的語言。一般使用async函數和await表達式實作異步操作。
Dart 庫提供asynchronous功能,該功能提供接口來進行耗費時間的操作,比如檔案讀寫、網絡請求。該功能傳回Future或Stream對象。
可以通過如下的方式來擷取asynchronous功能傳回的Future對象的值:
□使用async函數和await表達式。
□使用Future功能提供的API。
可以通過如下的方式來擷取asynchronous 功能傳回的Stream對象的值:
□使用async 和一個異步的循環(await for)。
□使用Stream的相關API。
下面的示例代碼使用了async或await異步處理,雖然代碼看起來像是同步處理的:
await readFile()
必須在一個使用了async關鍵字标記後的函數中使用await表達式:
fileOperate () async {
//讀取檔案
var file = await readFile();
//其他處理
3.11 中繼資料
使用中繼資料給代碼添加更多的資訊。中繼資料是以@開始的修飾符,在@後面接着編譯時的常量或調用一個常量構造函數。目前Dart語言提供三個@修飾符:
□@deprecated 被棄用的。
□@override 重寫。
□@proxy 代理。
使用@override修飾符可以重寫父類方法。改造之前寫的例子,人類重寫eat方法,代碼如下所示:
print('動物會吃');
//動物會跑
print('動物會跑');
print('人類會說');
print('人類會學習');
@override
//人類也會吃
print('人類也會吃');
上面的代碼輸出結果如下,會輸出一句“人類也會吃”,表明重寫了父類的方法:
flutter: 人類也會吃
中繼資料可以修飾library(庫)、class(類)、typedef(類型定義)、type parameter(類型參數)、constructor(構造函數)、factory(工廠函數)、function(函數)、field(作用域)、
parameter(參數)、variable declaration(變量聲明)。
3.12 注釋
Dart 支援三種注釋類型:單行注釋、多行注釋、文檔注釋。
1.單行注釋
單行注釋以//開頭,從//開始到一行結束的所有内容都會被Dart 編譯器忽略,示例代碼如下:
// 列印輸出
print('Hi Dart);
2.多行注釋
3.文檔注釋