目錄
- 參考資料
- 語言特性
- 關鍵字
- 變量與常量
- 數(shù)據(jù)類型
- 運算符 operators
- 控制流程語句
- 異常 Exceptions
- 函數(shù) Function
- 類 Class
- 類-方法
- 類-抽象類
- 類-隱式接口
- 類-擴展一個類(重寫)
- 庫和可見性
- 異步支持
參考資料
- https://www.dartlang.org/guides/language/language-tour 【官方文檔】
- http://wiki.jikexueyuan.com/project/dart-language-tour/classes.html 【極客學院】
- http://www.lxweimin.com/p/3d927a7bf020 【author:AWeiLoveAndroid】
- http://www.lxweimin.com/p/a7cc623132b0 【author:soojade】
- http://www.lxweimin.com/p/78d317b2ea79 【author:優(yōu)騰愛樂】
語言特性
Dart所有的東西都是對象, 即使是數(shù)字numbers、函數(shù)function、null也都是對象,所有的對象都繼承自Object類。
Dart動態(tài)類型語言, 盡量給變量定義一個類型,會更安全,沒有顯示定義類型的變量在 debug 模式下類型會是 dynamic(動態(tài)的)。
Dart 在 running 之前解析你的所有代碼,指定數(shù)據(jù)類型和編譯時的常量,可以提高運行速度。
Dart中的類和接口是統(tǒng)一的,類即接口,你可以繼承一個類,也可以實現(xiàn)一個類(接口),自然也包含了良好的面向對象和并發(fā)編程的支持。
Dart 提供了頂級函數(shù)(如:main())。
Dart 沒有 public、private、protected 這些關鍵字,變量名以"_"開頭意味著對它的 lib 是私有的。
沒有初始化的變量都會被賦予默認值 null。
final的值只能被設定一次。const 是一個編譯時的常量,可以通過 const 來創(chuàng)建常量值,var c=const[];,這里 c 還是一個變量,只是被賦值了一個常量值,它還是可以賦其它值。實例變量可以是 final,但不能是 const。
-
編程語言并不是孤立存在的,Dart也是這樣,他由語言規(guī)范、虛擬機、類庫和工具等組成:
- SDK:SDK 包含 Dart VM、dart2js、Pub、庫和工具。
- Dartium:內嵌 Dart VM 的 Chromium ,可以在瀏覽器中直接執(zhí)行 dart 代碼。
- Dart2js:將 Dart 代碼編譯為 JavaScript 的工具。
- Dart Editor:基于 Eclipse 的全功能 IDE,并包含以上所有工具。支持代碼補全、代碼導航、快速修正、重構、調試等功能。
關鍵字(56個)
關鍵字 | - | - | - |
---|---|---|---|
abstract | do | import | super |
as | dynamic | in | switch |
assert | else | interface | sync* |
enum | implements | is | this |
async* | export | library | throw |
await | external | mixin | true |
break | extends | new | try |
case | factory | null | typedef |
catch | false | operator | var |
class | final | part | void |
const | finally | rethrow | while |
continue | for | return | with |
covariant | get | set | yield* |
default | if | static | deferred |
變量與常量
- 變量聲明與初始化
- 調用的變量name包含對String值為“張三” 的對象的引用,name推斷變量的類型是String,但可以通過指定它來更改該類型,如果對象不限于單一類型(沒有明確的類型),請使用Object或dynamic關鍵字。
// 沒有明確類型,編譯的時候根據(jù)值明確類型
var name = ‘Bob’;
Object name = '張三';
dynamic name = '李四';
// 顯示聲明將被推斷類型, 可以使用String顯示聲明字符串類型
String name = 'Bob' ;
- 默認值
- 未初始化的變量的初始值為null(包括數(shù)字),因此數(shù)字、字符串都可以調用各種方法
//測試 數(shù)字類型的初始值是什么?
int lineCount;
// 為false的時候拋出異常
assert(lineCount == null);
print(lineCount); //打印結果為null,證明數(shù)字類型初始化值是null
-
final and const
如果您從未打算更改一個變量,那么使用 final 或 const,不是var,也不是一個類型。
一個 final 變量只能被初始化一次; const變量是一個編譯時常量,(Const變量是隱式的final)
final的頂級或類變量在第一次使用時被初始化。被final修飾的頂級變量或類變量在第一次聲明的時候就需要初始化。
// The final variable 'outSideFinalName' must be initialized. final String outSideFinalName
- 被final或者const修飾的變量,變量類型可以省略,建議指定數(shù)據(jù)類型。
//可以省略String這個類型聲明 final name = "Bob"; final String name1 = "張三"; const name2 = "alex"; const String name3 = "李四";
- 被 final 或 const 修飾的變量無法再去修改其值。
final String outSideFinalName = "Alex"; // outSideFinalName', a final variable, can only be set once // 一個final變量,只能被設置一次。 outSideFinalName = "Bill"; const String outSideName = 'Bill'; // 這樣寫,編譯器提示:Constant variables can't be assigned a value // const常量不能賦值 // outSideName = "小白";
- flnal 或者 const 不能和 var 同時使用
// Members can't be declared to be both 'const' and 'var' const var String outSideName = 'Bill'; // Members can't be declared to be both 'final' and 'var' final var String name = 'Lili';
- 常量如果是類級別的,請使用 static const
// 常量如果是類級別的,請使用 static const static const String name3 = 'Tom'; // 這樣寫保存 // Only static fields can be declared as const // 只有靜態(tài)字段可以聲明為const //const String name3 = 'Tom';
- 常量的運算
const speed = 100; //速度(km/h) const double distance = 2.5 * speed; // 距離 = 時間 * 速度 final speed2 = 100; //速度(km/h) final double distance2 = 2.5 * speed2; // 距離 = 時間 * 速度
- const關鍵字不只是聲明常數(shù)變量,您也可以使用它來創(chuàng)建常量值,以及聲明創(chuàng)建常量值的構造函數(shù),任何變量都可以有一個常量值。
// 注意: [] 創(chuàng)建的是一個空的list集合 // const []創(chuàng)建一個空的、不可變的列表(EIL)。 var varList = const []; // varList 當前是一個EIL final finalList = const []; // finalList一直是EIL const constList = const []; // constList 是一個編譯時常量的EIL // 可以更改非final,非const變量的值 // 即使它曾經具有const值 varList = ["haha"]; // 不能更改final變量或const變量的值 // 這樣寫,編譯器提示:a final variable, can only be set once // finalList = ["haha"]; // 這樣寫,編譯器提示:Constant variables can't be assigned a value // constList = ["haha"];
- 在常量表達式中,該運算符的操作數(shù)必須為'bool'、'num'、'String'或'null', const常量必須用conat類型的值初始化。
const String outSideName = 'Bill'; final String outSideFinalName = 'Alex'; const String outSideName2 = 'Tom'; const aConstList = const ['1', '2', '3']; // In constant expressions, operands of this operator must be of type 'bool', 'num', 'String' or 'null' // 在常量表達式中,該運算符的操作數(shù)必須為'bool'、'num'、'String'或'null'。 const validConstString = '$outSideName $outSideName2 $aConstList'; // Const variables must be initialized with a constant value // const常量必須用conat類型的值初始化 const validConstString = '$outSideName $outSideName2 $outSideFinalName'; var outSideVarName='Cathy'; // Const variables must be initialized with a constant value. // const常量必須用conat類型的值初始化 const validConstString = '$outSideName $outSideName2 $outSideVarName'; // 正確寫法 const String outSideConstName = 'Joy'; const validConstString = '$outSideName $outSideName2 $outSideConstName';
數(shù)據(jù)類型
-
num
num 是數(shù)字類型的父類,有兩個子類 int 和 double。
int 根據(jù)平臺的不同,整數(shù)值不大于64位。在Dart VM上,值可以從-263到263 - 1,編譯成JavaScript的Dart使用JavaScript代碼,允許值從-253到253 - 1。
double 64位(雙精度)浮點數(shù),如IEEE 754標準所規(guī)定。
int a = 1; print(a); double b = 1.12; print(b); // String -> int int one = int.parse('1'); // 輸出3 print(one + 2); // String -> double var onePointOne = double.parse('1.1'); // 輸出3.1 print(onePointOne + 2); // int -> String String oneAsString = 1.toString(); // The argument type 'int' can't be assigned to the parameter type 'String' //print(oneAsString + 2); // 輸出 1 + 2 print('$oneAsString + 2'); // 輸出 1 2 print('$oneAsString 2'); // double -> String 注意括號中要有小數(shù)點位數(shù),否則報錯 String piAsString = 3.14159.toStringAsFixed(2); // 截取兩位小數(shù), 輸出3.14 print(piAsString); String aString = 1.12618.toStringAsFixed(2); // 檢查是否四舍五入,輸出1.13,發(fā)現(xiàn)會做四舍五入 print(aString);
-
String
- Dart里面的String是一系列 UTF-16 代碼單元。
- 您可以使用單引號或雙引號來創(chuàng)建一個字符串。
- 單引號或者雙引號里面嵌套使用引號。
- 用
{} 來計算字符串中變量的值,需要注意的是如果是表達式需要${表達式}
String singleString = 'abcdddd'; String doubleString = "abcsdfafd"; String sdString = '$singleString a "bcsd" ${singleString}'; String dsString = "abc 'aaa' $sdString"; print(sdString); print(dsString); String singleString = 'aaa'; String doubleString = "bbb"; // 單引號嵌套雙引號 String sdString = '$singleString a "bbb" ${doubleString}'; // 輸出 aaa a "bbb" bbb print(sdString); // 雙引號嵌套單引號 String dsString = "${singleString.toUpperCase()} abc 'aaa' $doubleString.toUpperCase()"; // 輸出 AAA abc 'aaa' bbb.toUpperCase(), 可以看出 ”$doubleString.toUpperCase()“ 沒有加“{}“,導致輸出結果是”bbb.toUpperCase()“ print(dsString);
bool
- Dart 是強 bool 類型檢查,只有bool 類型的值是true 才被認為是true。
- 只有兩個對象具有bool類型:true和false,它們都是編譯時常量。
- Dart的類型安全意味著您不能使用 if(nonbooleanValue) 或 assert(nonbooleanValue) 等代碼, 相反Dart使用的是顯式的檢查值。
- assert 是語言內置的斷言函數(shù),僅在檢查模式下有效
在開發(fā)過程中, 除非條件為真,否則會引發(fā)異常。(斷言失敗則程序立刻終止)。
// 檢查是否為空字符串
var fullName = '';
assert(fullName.isEmpty);
// 檢查0
var hitPoints = 0;
assert(hitPoints <= 0);
// 檢查是否為null
var unicorn;
assert(unicorn == null);
// 檢查是否為NaN
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);
- List集合
- 在Dart中,數(shù)組是List對象,因此大多數(shù)人只是將它們稱為List。
Dart list文字看起來像JavaScript數(shù)組文字
//創(chuàng)建一個int類型的list
List list = [10, 7, 23];
// 輸出[10, 7, 23]
print(list);
// 使用List的構造函數(shù),也可以添加int參數(shù),表示List固定長度,不能進行添加 刪除操作
var fruits = new List();
// 添加元素
fruits.add('apples');
// 添加多個元素
fruits.addAll(['oranges', 'bananas']);
List subFruits = ['apples', 'oranges', 'banans'];
// 添加多個元素
fruits.addAll(subFruits);
// 輸出: [apples, oranges, bananas, apples, oranges, banans]
print(fruits);
// 獲取List的長度
print(fruits.length);
// 獲取第一個元素
print(fruits.first);
// 獲取元素最后一個元素
print(fruits.last);
// 利用索引獲取元素
print(fruits[0]);
// 查找某個元素的索引號
print(fruits.indexOf('apples'));
// 刪除指定位置的元素,返回刪除的元素
print(fruits.removeAt(0));
// 刪除指定元素,成功返回true,失敗返回false
// 如果集合里面有多個“apples”, 只會刪除集合中第一個改元素
fruits.remove('apples');
// 刪除最后一個元素,返回刪除的元素
fruits.removeLast();
// 刪除指定范圍(索引)元素,含頭不含尾
fruits.removeRange(start,end);
// 刪除指定條件的元素(這里是元素長度大于6)
fruits.removeWhere((item) => item.length >6);
// 刪除所有的元素
fruits.clear();
-
注意事項:
可以直接打印list包括list的元素,list也是一個對象。但是java必須遍歷才能打印list,直接打印是地址值。
和java一樣list里面的元素必須保持類型一致,不一致就會報錯。
和java一樣list的角標從0開始。
如果集合里面有多個相同的元素“X”, 只會刪除集合中第一個改元素
- Map集合
一般來說,map是將鍵和值相關聯(lián)的對象。鍵和值都可以是任何類型的對象。
每個鍵只出現(xiàn)一次,但您可以多次使用相同的值。Dart支持map由map文字和map類型提供。初始化Map方式一: 直接聲明,用{}表示,里面寫key和value,每組鍵值對中間用逗號隔開。
// Two keys in a map literal can't be equal.
// Map companys = {'Alibaba': '阿里巴巴', 'Tencent': '騰訊', 'baidu': '百度', 'Alibaba': '釘釘', 'Tenect': 'qq-music'};
Map companys = {'Alibaba': '阿里巴巴', 'Tencent': '騰訊', 'baidu': '百度'};
// 輸出:{Alibaba: 阿里巴巴, Tencent: 騰訊, baidu: 百度}
print(companys);
- 創(chuàng)建Map方式二:先聲明,再去賦值。
Map schoolsMap = new Map();
schoolsMap['first'] = '清華';
schoolsMap['second'] = '北大';
schoolsMap['third'] = '復旦';
// 打印結果 {first: 清華, second: 北大, third: 復旦}
print(schoolsMap);
var fruits = new Map();
fruits["first"] = "apple";
fruits["second"] = "banana";
fruits["fifth"] = "orange";
//換成雙引號,換成var 打印結果 {first: apple, second: banana, fifth: orange}
print(fruits);
- Map API
// 指定鍵值對的參數(shù)類型
var aMap = new Map<int, String>();
// Map的賦值,中括號中是Key,這里可不是數(shù)組
aMap[1] = '小米';
//Map中的鍵值對是唯一的
//同Set不同,第二次輸入的Key如果存在,Value會覆蓋之前的數(shù)據(jù)
aMap[1] = 'alibaba';
// map里面的value可以相同
aMap[2] = 'alibaba';
// map里面value可以為空字符串
aMap[3] = '';
// map里面的value可以為null
aMap[4] = null;
print(aMap);
// 檢索Map是否含有某Key
assert(aMap.containsKey(1));
//刪除某個鍵值對
aMap.remove(1);
print(aMap);
-
注意事項
map的key類型不一致也不會報錯。
添加元素的時候,會按照你添加元素的順序逐個加入到map里面,哪怕你的key,比如分別是 1,2,4,看起來有間隔,事實上添加到map的時候是{1:value,2:value,4:value} 這種形式。
map里面的key不能相同。但是value可以相同,value可以為空字符串或者為null。
運算符
描述 | 操作符 | ||
---|---|---|---|
一元后置操作符 |
expr++ expr-- () [] . ?.
|
||
一元前置操作符 |
expr !expr ~expr ++expr --expr
|
||
乘除 |
* / % ~/
|
||
加減 | + - | ||
位移 |
<< >>
|
||
按位與 | & | ||
按位或 | |||
按位異或 | ^ | ||
邏輯與 | && | ||
邏輯或 | |||
關系和類型判斷 |
>= > <= < as is is!
|
||
等 |
== !=
|
||
如果為空 | ?? |
||
條件表達式 | expr1 ? expr2 : expr3 |
||
賦值 |
= *= /= ~/= %= += -= <<= >>= &= ^= = ??=
|
||
級聯(lián) | .. |
流程控制語句(Control flow statements)
- if...else
- for
- while do-whild
- break continue
- switch...case
- assert(僅在checked模式有效)
異常(Exceptions)
- throw
- 拋出固定類型的異常
throw new FormatException('Expected at least 1 section');
-
拋出任意類型的異常
throw 'Out of llamas!';
-
因為拋出異常屬于表達式,可以將throw語句放在=>語句中,或者其它可以出現(xiàn)表達式的地方
distanceTo(Point other) => throw new UnimplementedError();
- catch
- 將可能出現(xiàn)異常的代碼放置到try語句中,可以通過 on語句來指定需要捕獲的異常類型,使用catch來處理異常。
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
- rethrow
- rethrow語句用來處理一個異常,同時希望這個異常能夠被其它調用的部分使用。
final foo = '';
void misbehave() {
try {
foo = "1";
} catch (e) {
print('2');
rethrow;// 如果不重新拋出異常,main函數(shù)中的catch語句執(zhí)行不到
}
}
void main() {
try {
misbehave();
} catch (e) {
print('3');
}
}
-
finally
- Dart的finally用來執(zhí)行那些無論異常是否發(fā)生都執(zhí)行的操作。
final foo = ''; void misbehave() { try { foo = "1"; } catch (e) { print('2'); } } void main() { try { misbehave(); } catch (e) { print('3'); } finally { print('4'); // 即使沒有rethrow最終都會執(zhí)行到 } }
函數(shù) Function
- 以下是一個實現(xiàn)函數(shù)的例子:
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
-
main()函數(shù)
- 每個應用程序都必須有一個頂層main()函數(shù),它可以作為應用程序的入口點。該main()函數(shù)返回void并具有List<String>參數(shù)的可選參數(shù)。
void main() { querySelector('#sample_text_id') ..text = 'Click me!' ..onClick.listen(reverseText); }
- 級聯(lián)符號..允許您在同一個對象上進行一系列操作。除了函數(shù)調用之外,還可以訪問同一對象上的字段。這通常會為您節(jié)省創(chuàng)建臨時變量的步驟,并允許您編寫更流暢的代碼。
querySelector('#confirm') // Get an object. ..text = 'Confirm' // Use its members. ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!'));
- 上述例子相對于:
var button = querySelector('#confirm'); button.text = 'Confirm'; button.classes.add('important'); button.onClick.listen((e) => window.alert('Confirmed!'));
- 級聯(lián)符號也可以嵌套使用。 例如:
final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build();
- 當返回值是void時不能構建級聯(lián)。 例如,以下代碼失敗:
var sb = StringBuffer(); sb.write('foo') // 返回void ..write('bar'); // 這里會報錯
- 注意: 嚴格地說,級聯(lián)的..符號不是操作符。它只是Dart語法的一部分。
-
可選參數(shù)
- 可選的命名參數(shù), 定義函數(shù)時,使用{param1, param2, …},用于指定命名參數(shù)。例如:
//設置[bold]和[hidden]標志 void enableFlags({bool bold, bool hidden}) { // ... } enableFlags(bold: true, hidden: false);
- 可選的位置參數(shù),用[]它們標記為可選的位置參數(shù):
String say(String from, String msg, [String device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; }
- 下面是一個不帶可選參數(shù)調用這個函數(shù)的例子:
say('Bob', 'Howdy'); //結果是: Bob says Howdy
- 下面是用第三個參數(shù)調用這個函數(shù)的例子:
say('Bob', 'Howdy', 'smoke signal'); //結果是:Bob says Howdy with a smoke signal
-
默認參數(shù)
函數(shù)可以使用=為命名參數(shù)和位置參數(shù)定義默認值。默認值必須是編譯時常量。如果沒有提供默認值,則默認值為null。
下面是為命名參數(shù)設置默認值的示例:
// 設置 bold 和 hidden 標記的默認值都為false void enableFlags2({bool bold = false, bool hidden = false}) { // ... } // 調用的時候:bold will be true; hidden will be false. enableFlags2(bold: true);
- 下一個示例顯示如何為位置參數(shù)設置默認值:
String say(String from, String msg, [String device = 'carrier pigeon', String mood]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } if (mood != null) { result = '$result (in a $mood mood)'; } return result; } //調用方式: say('Bob', 'Howdy'); //結果為:Bob says Howdy with a carrier pigeon;
- 您還可以將list或map作為默認值傳遞。下面的示例定義一個函數(shù)doStuff(),該函數(shù)指定列表參數(shù)的默認list和gifts參數(shù)的默認map。
// 使用list 或者map設置默認值 void doStuff( {List<int> list = const [1, 2, 3], Map<String, String> gifts = const {'first': 'paper', 'second': 'cotton', 'third': 'leather' }}) { print('list: $list'); print('gifts: $gifts'); }
作為一個類對象的功能
- 您可以將一個函數(shù)作為參數(shù)傳遞給另一個函數(shù)。
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// 把 printElement函數(shù)作為一個參數(shù)傳遞進來
list.forEach(printElement);
- 您也可以將一個函數(shù)分配給一個變量。
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
-
匿名函數(shù)
大多數(shù)函數(shù)都能被命名為匿名函數(shù),如 main() 或 printElement()。您還可以創(chuàng)建一個名為匿名函數(shù)的無名函數(shù),有時也可以創(chuàng)建lambda或閉包。您可以為變量分配一個匿名函數(shù),例如,您可以從集合中添加或刪除它。
一個匿名函數(shù)看起來類似于一個命名函數(shù) - 0或更多的參數(shù),在括號之間用逗號和可選類型標注分隔。
下面的代碼塊包含函數(shù)的主體:
([[Type] param1[, …]]) { codeBlock; };
- 下面的示例定義了一個具有無類型參數(shù)的匿名函數(shù)item,該函數(shù)被list中的每個item調用,輸出一個字符串,該字符串包含指定索引處的值。
var list = ['apples', 'bananas', 'oranges']; list.forEach((item) { print('${list.indexOf(item)}: $item'); });
- 如果函數(shù)只包含一條語句,可以使用箭頭符號=>來縮短它, 比如上面的例2可以簡寫成:
list.forEach((item) => print('${list.indexOf(item)}: $item'));
返回值
- 所有函數(shù)都返回一個值,如果沒有指定返回值,則語句return null,隱式地附加到函數(shù)體。
foo() {}
assert(foo() == null);
類(Classes)
- 對象
- Dart 是一種面向對象的語言,并且支持基于mixin的繼承方式。
- Dart 語言中所有的對象都是某一個類的實例,所有的類有同一個基類--Object。
- 基于mixin的繼承方式具體是指:一個類可以繼承自多個父類。
- 使用new語句來構造一個類,構造函數(shù)的名字可能是ClassName,也可以是ClassName.identifier, 例如:
var jsonData = JSON.decode('{"x":1, "y":2}');
// Create a Point using Point().
var p1 = new Point(2, 2);
// Create a Point using Point.fromJson().
var p2 = new Point.fromJson(jsonData);
- 使用.(dot)來調用實例的變量或者方法。
var p = new Point(2, 2);
// Set the value of the instance variable y.
p.y = 3;
// Get the value of y.
assert(p.y == 3);
// Invoke distanceTo() on p.
num distance = p.distanceTo(new Point(4, 4));
- 使用
?.
來確認前操作數(shù)不為空, 常用來替代.
, 避免左邊操作數(shù)為null引發(fā)異常。
```
// If p is non-null, set its y value to 4.
p?.y = 4;
```
- 使用const替代new來創(chuàng)建編譯時的常量構造函數(shù)。
```
var p = const ImmutablePoint(2, 2);
```
- 使用runtimeType方法,在運行中獲取對象的類型。該方法將返回Type 類型的變量。
```
print('The type of a is ${a.runtimeType}');
```
- 實例化變量(Instance variables)
- 在類定義中,所有沒有初始化的變量都會被初始化為null。
class Point {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}
- 類定義中所有的變量, Dart語言都會隱式的定義 setter 方法,針對非空的變量會額外增加 getter 方法。
class Point {
num x;
num y;
}
main() {
var point = new Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}
- 構造函數(shù)(Constructors)
- 聲明一個和類名相同的函數(shù),來作為類的構造函數(shù)。
class Point {
num x;
num y;
Point(num x, num y) {
// There's a better way to do this, stay tuned.
this.x = x;
this.y = y;
}
}
- this關鍵字指向了當前類的實例, 上面的代碼可以簡化為:
class Point {
num x;
num y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
- 構造函數(shù)不能繼承(Constructors aren’t inherited)
- Dart 語言中,子類不會繼承父類的命名構造函數(shù)。如果不顯式提供子類的構造函數(shù),系統(tǒng)就提供默認的構造函數(shù)。
- 命名的構造函數(shù)(Named constructors)
- 使用命名構造函數(shù)從另一類或現(xiàn)有的數(shù)據(jù)中快速實現(xiàn)構造函數(shù)。
class Point {
num x;
num y;
Point(this.x, this.y);
// 命名構造函數(shù)Named constructor
Point.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
- 構造函數(shù)不能被繼承,父類中的命名構造函數(shù)不能被子類繼承。如果想要子類也擁有一個父類一樣名字的構造函數(shù),必須在子類是實現(xiàn)這個構造函數(shù)。
- 調用父類的非默認構造函數(shù)
-
默認情況下,子類只能調用父類的無名,無參數(shù)的構造函數(shù); 父類的無名構造函數(shù)會在子類的構造函數(shù)前調用; 如果initializer list 也同時定義了,則會先執(zhí)行initializer list 中的內容,然后在執(zhí)行父類的無名無參數(shù)構造函數(shù),最后調用子類自己的無名無參數(shù)構造函數(shù)。即下面的順序:
- initializer list(初始化列表)
- super class’s no-arg constructor(父類無參數(shù)構造函數(shù))
- main class’s no-arg constructor (主類無參數(shù)構造函數(shù))
-
如果父類不顯示提供無名無參數(shù)構造函數(shù)的構造函數(shù),在子類中必須手打調用父類的一個構造函數(shù)。這種情況下,調用父類的構造函數(shù)的代碼放在子類構造函數(shù)名后,子類構造函數(shù)體前,中間使用
:(colon)
分割。class Person { String firstName; Person.fromJson(Map data) { print('in Person'); } } class Employee extends Person { // 父類沒有無參數(shù)的非命名構造函數(shù),必須手動調用一個構造函數(shù) super.fromJson(data) Employee.fromJson(Map data) : super.fromJson(data) { print('in Employee'); } } main() { var emp = new Employee.fromJson({}); // Prints: // in Person // in Employee if (emp is Person) { // Type check emp.firstName = 'Bob'; } (emp as Person).firstName = 'Bob'; }
-
初始化列表
- 除了調用父類的構造函數(shù),也可以通過初始化列表在子類的構造函數(shù)體前(大括號前)來初始化實例的變量值,使用逗號,分隔。如下所示:
class Point { num x; num y; Point(this.x, this.y); // 初始化列表在構造函數(shù)運行前設置實例變量。 Point.fromJson(Map jsonMap) : x = jsonMap['x'], y = jsonMap['y'] { print('In Point.fromJson(): ($x, $y)'); } }
注意:上述代碼,初始化程序無法訪問 this 關鍵字。
-
靜態(tài)構造函數(shù)
- 如果你的類產生的對象永遠不會改變,你可以讓這些對象成為編譯時常量。為此,需要定義一個 const 構造函數(shù)并確保所有的實例變量都是 final 的。
class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); static final ImmutablePoint origin = const ImmutablePoint(0, 0); }
-
重定向構造函數(shù)
- 有時候構造函數(shù)的目的只是重定向到該類的另一個構造函數(shù)。重定向構造函數(shù)沒有函數(shù)體,使用冒號:分隔。
class Point { num x; num y; // 主構造函數(shù) Point(this.x, this.y) { print("Point($x, $y)"); } // 重定向構造函數(shù),指向主構造函數(shù),函數(shù)體為空 Point.alongXAxis(num x) : this(x, 0); } void main() { var p1 = new Point(1, 2); var p2 = new Point.alongXAxis(4); }
常量構造函數(shù)
-
如果類的對象不會發(fā)生變化,可以構造一個編譯時的常量構造函數(shù)。定義格式如下:
- 定義所有的實例變量是final。
- 使用const聲明構造函數(shù)。
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin = const ImmutablePoint(0, 0);
}
- 工廠構造函數(shù)
- 當實現(xiàn)一個使用 factory 關鍵詞修飾的構造函數(shù)時,這個構造函數(shù)不必創(chuàng)建類的新實例。例如,工廠構造函數(shù)可能從緩存返回實例,或者它可能返回子類型的實例。 下面的示例演示一個工廠構造函數(shù)從緩存返回的對象:
class Logger {
final String name;
bool mute = false;
// _cache 是一個私有庫,幸好名字前有個 _ 。
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) {
print(msg);
}
}
}
注意:工廠構造函數(shù)不能用 this。
方法
- 方法就是為對象提供行為的函數(shù)。
-
實例方法
- 對象的實例方法可以訪問實例變量和 this 。以下示例中的 distanceTo() 方法是實例方法的一個例子:
import 'dart:math'; class Point { num x; num y; Point(this.x, this.y); num distanceTo(Point other) { var dx = x - other.x; var dy = y - other.y; return sqrt(dx * dx + dy * dy); } }
-
setters 和 Getters
- 是一種提供對方法屬性讀和寫的特殊方法。每個實例變量都有一個隱式的 getter 方法,合適的話可能還會有 setter 方法。你可以通過實現(xiàn) getters 和 setters 來創(chuàng)建附加屬性,也就是直接使用 get 和 set 關鍵詞:
class Rectangle { num left; num top; num width; num height; Rectangle(this.left, this.top, this.width, this.height); // 定義兩個計算屬性: right and bottom. num get right => left + width; set right(num value) => left = value - width; num get bottom => top + height; set bottom(num value) => top = value - height; } main() { var rect = new Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); }
借助于 getter 和 setter ,你可以直接使用實例變量,并且在不改變客戶代碼的情況下把他們包裝成方法。
注: 不論是否顯式地定義了一個 getter,類似增量(++)的操作符,都能以預期的方式工作。為了避免產生任何向著不期望的方向的影響,操作符一旦調用 getter ,就會把他的值存在臨時變量里。
-
抽象方法
- Instance , getter 和 setter 方法可以是抽象的,也就是定義一個接口,但是把實現(xiàn)交給其他的類。要創(chuàng)建一個抽象方法,使用分號(;)代替方法體:
abstract class Doer { // ...定義實例變量和方法... void doSomething(); // 定義一個抽象方法。 } class EffectiveDoer extends Doer { void doSomething() { // ...提供一個實現(xiàn),所以這里的方法不是抽象的... } }
枚舉類型
枚舉類型,通常被稱為 enumerations 或 enums ,是一種用來代表一個固定數(shù)量的常量的特殊類。
-
聲明一個枚舉類型需要使用關鍵字 enum :
enum Color { red, green, blue }
在枚舉中每個值都有一個 index getter 方法,它返回一個在枚舉聲明中從 0 開始的位置。例如,第一個值索引值為 0 ,第二個值索引值為 1 。
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
- 要得到枚舉列表的所有值,可使用枚舉的 values 常量。
```
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
```
* 你可以在 switch 語句 中使用枚舉。如果 e 在 switch (e) 是顯式類型的枚舉,那么如果你不處理所有的枚舉值將會彈出警告:
```
enum Color {
red,
green,
blue
}
// ...
Color 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'
}
```
***枚舉類型有以下限制***
* 你不能在子類中混合或實現(xiàn)一個枚舉。
* 你不能顯式實例化一個枚舉。
-
為類添加特征:mixins
mixins 是一種多類層次結構的類的代碼重用。
要使用 mixins ,在 with 關鍵字后面跟一個或多個 mixin 的名字。下面的例子顯示了兩個使用mixins的類:
class Musician extends Performer with Musical { // ... } class Maestro extends Person with Musical, Aggressive, Demented { Maestro(String maestroName) { name = maestroName; canConduct = true; } }
-
要實現(xiàn) mixin ,就創(chuàng)建一個繼承 Object 類的子類,不聲明任何構造函數(shù),不調用 super 。例如:
abstract class Musical { bool canPlayPiano = false; bool canCompose = false; bool canConduct = false; void entertainMe() { if (canPlayPiano) { print('Playing piano'); } else if (canConduct) { print('Waving hands'); } else { print('Humming to self'); } } }
-
類的變量和方法
使用 static 關鍵字來實現(xiàn)類變量和類方法。
只有當靜態(tài)變量被使用時才被初始化。
-
靜態(tài)變量, 靜態(tài)變量(類變量)對于類狀態(tài)和常數(shù)是有用的:
class Color { static const red = const Color('red'); // 一個恒定的靜態(tài)變量 final String name; // 一個實例變量。 const Color(this.name); // 一個恒定的構造函數(shù)。 } main() { assert(Color.red.name == 'red'); }
靜態(tài)方法, 靜態(tài)方法(類方法)不在一個實例上進行操作,因而不必訪問 this 。例如:
import 'dart:math'; class Point { num x; num y; Point(this.x, this.y); static num distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } } main() { var a = new Point(2, 2); var b = new Point(4, 4); var distance = Point.distanceBetween(a, b); assert(distance < 2.9 && distance > 2.8); }
注:考慮到使用高階層的方法而不是靜態(tài)方法,是為了常用或者廣泛使用的工具和功能。
你可以將靜態(tài)方法作為編譯時常量。例如,你可以把靜態(tài)方法作為一個參數(shù)傳遞給靜態(tài)構造函數(shù)。
抽象類
使用 abstract 修飾符來定義一個抽象類,該類不能被實例化。抽象類在定義接口的時候非常有用,實際上抽象中也包含一些實現(xiàn)。如果你想讓你的抽象類被實例化,請定義一個 工廠構造函數(shù) 。
-
抽象類通常包含 抽象方法。下面是聲明一個含有抽象方法的抽象類的例子:
// 這個類是抽象類,因此不能被實例化。 abstract class AbstractContainer { // ...定義構造函數(shù),域,方法... void updateChildren(); // 抽象方法。 }
-
下面的類不是抽象類,因此它可以被實例化,即使定義了一個抽象方法:
class SpecializedContainer extends AbstractContainer { // ...定義更多構造函數(shù),域,方法... void updateChildren() { // ...實現(xiàn) updateChildren()... } // 抽象方法造成一個警告,但是不會阻止實例化。 void doSomething(); }
類-隱式接口
每個類隱式的定義了一個接口,含有類的所有實例和它實現(xiàn)的所有接口。如果你想創(chuàng)建一個支持類 B 的 API 的類 A,但又不想繼承類 B ,那么,類 A 應該實現(xiàn)類 B 的接口。
-
一個類實現(xiàn)一個或更多接口通過用 implements 子句聲明,然后提供 API 接口要求。例如:
// 一個 person ,包含 greet() 的隱式接口。 class Person { // 在這個接口中,只有庫中可見。 final _name; // 不在接口中,因為這是個構造函數(shù)。 Person(this._name); // 在這個接口中。 String greet(who) => 'Hello, $who. I am $_name.'; } // Person 接口的一個實現(xiàn)。 class Imposter implements Person { // 我們不得不定義它,但不用它。 final _name = ""; String greet(who) => 'Hi $who. Do you know who I am?'; } greetBob(Person person) => person.greet('bob'); main() { print(greetBob(new Person('kathy'))); print(greetBob(new Imposter())); }
-
這里是具體說明一個類實現(xiàn)多個接口的例子:
class Point implements Comparable, Location { // ... }
-
類-擴展一個類
-
使用 extends 創(chuàng)建一個子類,同時 supper 將指向父類:
class Television { void turnOn() { _illuminateDisplay(); _activateIrSensor(); } // ... } class SmartTelevision extends Television { void turnOn() { super.turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } // ... }
-
子類可以重載實例方法, getters 方法, setters 方法。下面是個關于重寫 Object 類的方法 noSuchMethod() 的例子,當代碼企圖用不存在的方法或實例變量時,這個方法會被調用。
class A { // 如果你不重寫 noSuchMethod 方法, 就用一個不存在的成員,會導致NoSuchMethodError 錯誤。 void noSuchMethod(Invocation mirror) { print('You tried to use a non-existent member:' + '${mirror.memberName}'); } }
-
你可以使用 @override 注釋來表明你重寫了一個成員。
class A { @override void noSuchMethod(Invocation mirror) { // ... } }
- 如果你用 noSuchMethod() 實現(xiàn)每一個可能的 getter 方法,setter 方法和類的方法,那么你可以使用 @proxy 標注來避免警告。
@proxy class A { void noSuchMethod(Invocation mirror) { // ... } }
庫和可見性
import,part,library指令可以幫助創(chuàng)建一個模塊化的,可共享的代碼庫。庫不僅提供了API,還提供隱私單元:以下劃線(_)開頭的標識符只對內部庫可見。每個Dartapp就是一個庫,即使它不使用庫指令。
庫可以分布式使用包。見 Pub Package and Asset Manager 中有關pub(SDK中的一個包管理器)。
使用庫
使用 import 來指定如何從一個庫命名空間用于其他庫的范圍。
例如,Dart Web應用一般采用這個庫 dart:html,可以這樣導入:
import 'dart:html';
- 唯一需要 import 的參數(shù)是一個指向庫的 URI。對于內置庫,URI中具有特殊dart:scheme。對于其他庫,你可以使用文件系統(tǒng)路徑或package:scheme。包 package:scheme specifies libraries ,如pub工具提供的軟件包管理器庫。例如:
import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';
- 指定庫前綴
- 如果導入兩個庫是有沖突的標識符,那么你可以指定一個或兩個庫的前綴。例如,如果 library1 和 library2 都有一個元素類,那么你可能有這樣的代碼:
```
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
var element1 = new Element(); // 使用lib1里的元素
var element2 =
new lib2.Element(); // 使用lib2里的元素
```
-
導入部分庫
- 如果想使用的庫一部分,你可以選擇性導入庫。例如:
// 只導入foo庫 import 'package:lib1/lib1.dart' show foo; //導入所有除了foo import 'package:lib2/lib2.dart' hide foo;
-
延遲加載庫
-
延遲(deferred)加載(也稱為延遲(lazy)加載)允許應用程序按需加載庫。下面是當你可能會使用延遲加載某些情況:
- 為了減少應用程序的初始啟動時間;
- 執(zhí)行A / B測試-嘗試的算法的替代實施方式中;
- 加載很少使用的功能,例如可選的屏幕和對話框。
-
為了延遲加載一個庫,你必須使用 deferred as 先導入它。
import 'package:deferred/hello.dart' deferred as hello;
-
當需要庫時,使用該庫的調用標識符調用 LoadLibrary()。
greet() async { await hello.loadLibrary(); hello.printGreeting(); }
在前面的代碼,在庫加載好之前,await關鍵字都是暫停執(zhí)行的。有關 async 和 await 見 asynchrony support 的更多信息。
您可以在一個庫調用 LoadLibrary() 多次都沒有問題。該庫也只被加載一次。
-
當您使用延遲加載,請記住以下內容:
- 延遲庫的常量在其作為導入文件時不是常量。記住,這些常量不存在,直到遲庫被加載完成。
- 你不能在導入文件中使用延遲庫常量的類型。相反,考慮將接口類型移到同時由延遲庫和導入文件導入的庫。
- Dart隱含調用LoadLibrary()插入到定義deferred as namespace。在調用LoadLibrary()函數(shù)返回一個Future。
-
-
庫的實現(xiàn)
- 用 library 來來命名庫,用part來指定庫中的其他文件。 注意:不必在應用程序中(具有頂級main()函數(shù)的文件)使用library,但這樣做可以讓你在多個文件中執(zhí)行應用程序。
-
聲明庫
-
利用library identifier(庫標識符)指定當前庫的名稱:
// 聲明庫,名ballgame library ballgame; // 導入html庫 import 'dart:html'; // ...代碼從這里開始...
-
-
關聯(lián)文件與庫
添加實現(xiàn)文件,把part fileUri放在有庫的文件,其中fileURI是實現(xiàn)文件的路徑。然后在實現(xiàn)文件中,添加部分標識符(part of identifier),其中標識符是庫的名稱。下面的示例使用的一部分,在三個文件來實現(xiàn)部分庫。
-
第一個文件,ballgame.dart,聲明球賽庫,導入其他需要的庫,并指定ball.dart和util.dart是此庫的部分:
library ballgame; import 'dart:html'; // ...其他導入在這里... part 'ball.dart'; part 'util.dart'; // ...代碼從這里開始...
-
第二個文件ball.dart,實現(xiàn)了球賽庫的一部分:
part of ballgame; // ...代碼從這里開始...
-
第三個文件,util.dart,實現(xiàn)了球賽庫的其余部分:
part of ballgame; // ...Code goes here...
- 重新導出庫(Re-exporting libraries)
* 可以通過重新導出部分庫或者全部庫來組合或重新打包庫。例如,你可能有實現(xiàn)為一組較小的庫集成為一個較大庫。或者你可以創(chuàng)建一個庫,提供了從另一個庫方法的子集。
```
// In french.dart:
library french;
hello() => print('Bonjour!');
goodbye() => print('Au Revoir!');
// In togo.dart:
library togo;
import 'french.dart';
export 'french.dart' show hello;
// In another .dart file:
import 'togo.dart';
void main() {
hello(); //print bonjour
goodbye(); //FAIL
}
```
異步的支持
Dart 添加了一些新的語言特性用于支持異步編程。最通常使用的特性是 async 方法和 await 表達式。Dart 庫大多方法返回 Future 和 Stream 對象。這些方法是異步的:它們在設置一個可能的耗時操作(比如 I/O 操作)之后返回,而無需等待操作完成
-
當你需要使用 Future 來表示一個值時,你有兩個選擇。
- 使用 async 和 await
- 使用 Future API
-
同樣的,當你需要從 Stream 獲取值的時候,你有兩個選擇。
- 使用 async 和一個異步的 for 循環(huán) (await for)
- 使用 Stream API
使用 async 和 await 的代碼是異步的,不過它看起來很像同步的代碼。比如這里有一段使用 await 等待一個異步函數(shù)結果的代碼:
await lookUpVersion()
-
要使用 await,代碼必須用 await 標記
checkVersion() async { var version = await lookUpVersion(); if (version == expectedVersion) { // Do something. } else { // Do something else. } }
-
你可以使用 try, catch, 和 finally 來處理錯誤并精簡使用了 await 的代碼。
try { server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4044); } catch (e) { // React to inability to bind to the port... }
聲明異步函數(shù)
-
一個異步函數(shù)是一個由 async 修飾符標記的函數(shù)。雖然一個異步函數(shù)可能在操作上比較耗時,但是它可以立即返回-在任何方法體執(zhí)行之前。
checkVersion() async { // ... } lookUpVersion() async => /* ... */;
在函數(shù)中添加關鍵字 async 使得它返回一個 Future,比如,考慮一下這個同步函數(shù),它將返回一個字符串。
String lookUpVersionSync() => '1.0.0';
如果你想更改它成為異步方法-因為在以后的實現(xiàn)中將會非常耗時-它的返回值是一個 Future 。
Future<String> lookUpVersion() async => '1.0.0';
請注意函數(shù)體不需要使用 Future API,如果必要的話 Dart 將會自己創(chuàng)建 Future 對象
- 使用帶 future 的 await 表達式
-
一個 await表達式具有以下形式
await expression
-
在異步方法中你可以使用 await 多次。比如,下列代碼為了得到函數(shù)的結果一共等待了三次。
var entrypoint = await findEntrypoint(); var exitCode = await runExecutable(entrypoint, args); await flushThenExit(exitCode);
在
await 表達式
中,表達式
的值通常是一個 Future 對象;如果不是,那么這個值會自動轉為 Future。這個 Future 對象表明了表達式應該返回一個對象。await 表達式
的值就是返回的一個對象。在對象可用之前,await 表達式
將會一直處于暫停狀態(tài)。如果 await 沒有起作用,請確認它是一個異步方法。比如,在你的 main() 函數(shù)里面使用await,main() 的函數(shù)體必須被 async 標記:
```
main() async {
checkVersion();
print('In main: version is ${await lookUpVersion()}');
}
```
-
結合 streams 使用異步循環(huán)
- 一個異步循環(huán)具有以下形式:
await for (variable declaration in expression) { // Executes each time the stream emits a value. }
-
表達式
的值必須有Stream 類型(流類型)。執(zhí)行過程如下:- 在 stream 發(fā)出一個值之前等待
- 執(zhí)行 for 循環(huán)的主體,把變量設置為發(fā)出的值。
- 重復 1 和 2,直到 Stream 關閉
如果要停止監(jiān)聽 stream ,你可以使用 break 或者 return 語句,跳出循環(huán)并取消來自 stream 的訂閱 。
如果一個異步 for 循環(huán)沒有正常運行,請確認它是一個異步方法。 比如,在應用的 main() 方法中使用異步的 for 循環(huán)時,main() 的方法體必須被 async 標記。
main() async { ... await for (var request in requestServer) { handleRequest(request); } ... }
- 更多關于異步編程的信息,請看 dart:async 庫部分的介紹。你也可以看文章 [Dart Language Asynchrony Support: Phase 1 ] (https://www.dartlang.org/articles/await-async/) 和 Dart Language Asynchrony Support: Phase 2 和 the Dart language specification