Dart全部的東西都是對象, 即便是數字numbers、函數function、null也都是對象,全部的對象都繼承自Object類。javascript
Dart動態類型語言, 儘可能給變量定義一個類型,會更安全,沒有顯示定義類型的變量在 debug 模式下會類型會是 dynamic(動態的)。php
Dart 在 running 以前解析你的全部代碼,指定數據類型和編譯時的常量,能夠提升運行速度。html
Dart中的類和接口是統一的,類即接口,你能夠繼承一個類,也能夠實現一個類(接口),天然也包含了良好的面向對象和併發編程的支持。java
Dart 提供了頂級函數(如:main())。算法
Dart 沒有 public、private、protected 這些關鍵字,變量名以"_"開頭意味着對它的 lib 是私有的。express
沒有初始化的變量都會被賦予默認值 null。編程
final的值只能被設定一次。const 是一個編譯時的常量,能夠經過 const 來建立常量值,var c=const[];,這裏 c 仍是一個變量,只是被賦值了一個常量值,它仍是能夠賦其它值。實例變量能夠是 final,但不能是 const。json
編程語言並非孤立存在的,Dart也是這樣,他由語言規範、虛擬機、類庫和工具等組成:數組
關鍵字 | - | - | - |
---|---|---|---|
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 |
// 沒有明確類型,編譯的時候根據值明確類型 var name = ‘Bob’; Object name = '張三'; dynamic name = '李四'; // 顯示聲明將被推斷類型, 可使用String顯示聲明字符串類型 String name = 'Bob' ;
//測試 數字類型的初始值是什麼? int lineCount; // 爲false的時候拋出異常 assert(lineCount == null); print(lineCount); //打印結果爲null,證實數字類型初始化值是null
final and const瀏覽器
若是您從未打算更改一個變量,那麼使用 final 或 const,不是var,也不是一個類型。
一個 final 變量只能被初始化一次; const變量是一個編譯時常量,(Const變量是隱式的final)
final的頂級或類變量在第一次使用時被初始化。
被final修飾的頂級變量或類變量在第一次聲明的時候就須要初始化。
// The final variable 'outSideFinalName' must be initialized. final String outSideFinalName
//能夠省略String這個類型聲明 final name = "Bob"; final String name1 = "張三"; const name2 = "alex"; const String name3 = "李四";
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 = "小白";
// 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 String name3 = 'Tom'; // 這樣寫保存 // Only static fields can be declared as const // 只有靜態字段能夠聲明爲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; // 距離 = 時間 * 速度
// 注意: [] 建立的是一個空的list集合 // const []建立一個空的、不可變的列表(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"];
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' // 在常量表達式中,該運算符的操做數必須爲'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';
num
num 是數字類型的父類,有兩個子類 int 和 double。
int 根據平臺的不一樣,整數值不大於64位。在Dart VM上,值能夠從-263到263 - 1,編譯成JavaScript的Dart使用JavaScript代碼,容許值從-253到253 - 1。
double 64位(雙精度)浮點數,如IEEE 754標準所規定。
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 注意括號中要有小數點位數,不然報錯 String piAsString = 3.14159.toStringAsFixed(2); // 截取兩位小數, 輸出3.14 print(piAsString); String aString = 1.12618.toStringAsFixed(2); // 檢查是否四捨五入,輸出1.13,發現會作四捨五入 print(aString);
String
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
// 檢查是否爲空字符串 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);
//建立一個int類型的list List list = [10, 7, 23]; // 輸出[10, 7, 23] print(list); // 使用List的構造函數,也能夠添加int參數,表示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是將鍵和值相關聯的對象。鍵和值均可以是任何類型的對象。
每一個鍵只出現一次,但您能夠屢次使用相同的值。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);
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);
// 指定鍵值對的參數類型 var aMap = new Map<int, String>(); // Map的賦值,中括號中是Key,這裏可不是數組 aMap[1] = '小米'; //Map中的鍵值對是惟一的 //同Set不一樣,第二次輸入的Key若是存在,Value會覆蓋以前的數據 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 |
||
賦值 | = *= /= ~/= %= += -= <<= >>= &= ^= = ??= |
||
級聯 | .. |
throw new FormatException('Expected at least 1 section');
拋出任意類型的異常
throw 'Out of llamas!';
由於拋出異常屬於表達式,能夠將throw語句放在=>語句中,或者其它能夠出現表達式的地方
distanceTo(Point other) =>
throw new UnimplementedError();
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'); }
final foo = ''; void misbehave() { try { foo = "1"; } catch (e) { print('2'); rethrow;// 若是不從新拋出異常,main函數中的catch語句執行不到 } } void main() { try { misbehave(); } catch (e) { print('3'); } }
finally
final foo = ''; void misbehave() { try { foo = "1"; } catch (e) { print('2'); } } void main() { try { misbehave(); } catch (e) { print('3'); } finally { print('4'); // 即便沒有rethrow最終都會執行到 } }
bool isNoble(int atomicNumber) { return _nobleGases[atomicNumber] != null; }
main()函數
void main() { querySelector('#sample_text_id') ..text = 'Click me!' ..onClick.listen(reverseText); }
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!'));
final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build();
var sb = StringBuffer(); sb.write('foo') // 返回void ..write('bar'); // 這裏會報錯
可選參數
//設置[bold]和[hidden]標誌 void enableFlags({bool bold, bool hidden}) { // ... } enableFlags(bold: true, hidden: false);
String say(String from, String msg, [String device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; }
say('Bob', 'Howdy'); //結果是: Bob says Howdy
say('Bob', 'Howdy', 'smoke signal'); //結果是:Bob says Howdy with a smoke signal
默認參數
函數可使用=爲命名參數和位置參數定義默認值。默認值必須是編譯時常量。若是沒有提供默認值,則默認值爲null。
下面是爲命名參數設置默認值的示例:
// 設置 bold 和 hidden 標記的默認值都爲false void enableFlags2({bool bold = false, bool hidden = false}) { // ... } // 調用的時候:bold will be true; hidden will be false. enableFlags2(bold: true);
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設置默認值 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'); }
做爲一個類對象的功能
void printElement(int element) { print(element); } var list = [1, 2, 3]; // 把 printElement函數做爲一個參數傳遞進來 list.forEach(printElement);
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!'; assert(loudify('hello') == '!!! HELLO !!!');
匿名函數
大多數函數都能被命名爲匿名函數,如 main() 或 printElement()。您還能夠建立一個名爲匿名函數的無名函數,有時也能夠建立lambda或閉包。您能夠爲變量分配一個匿名函數,例如,您能夠從集合中添加或刪除它。
一個匿名函數看起來相似於一個命名函數 - 0或更多的參數,在括號之間用逗號和可選類型標註分隔。
下面的代碼塊包含函數的主體:
([[Type] param1[, …]]) { codeBlock; };
var list = ['apples', 'bananas', 'oranges']; list.forEach((item) { print('${list.indexOf(item)}: $item'); });
list.forEach((item) => print('${list.indexOf(item)}: $item'));
返回值
foo() {}
assert(foo() == null);
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);
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));
?.
來確認前操做數不爲空, 經常使用來替代.
, 避免左邊操做數爲null引起異常。``` // If p is non-null, set its y value to 4. p?.y = 4; ```
``` var p = const ImmutablePoint(2, 2); ```
``` print('The type of a is ${a.runtimeType}'); ```
class Point { num x; // Declare instance variable x, initially null. num y; // Declare y, initially null. num z = 0; // Declare z, initially 0. }
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. }
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; } }
class Point { num x; num y; // Syntactic sugar for setting x and y // before the constructor body runs. Point(this.x, this.y); }
class Point { num x; num y; Point(this.x, this.y); // 命名構造函數Named constructor Point.fromJson(Map json) { x = json['x']; y = json['y']; } }
默認狀況下,子類只能調用父類的無名,無參數的構造函數; 父類的無名構造函數會在子類的構造函數前調用; 若是initializer list 也同時定義了,則會先執行initializer list 中的內容,而後在執行父類的無名無參數構造函數,最後調用子類本身的無名無參數構造函數。即下面的順序:
若是父類不顯示提供無名無參數構造函數的構造函數,在子類中必須手打調用父類的一個構造函數。這種狀況下,調用父類的構造函數的代碼放在子類構造函數名後,子類構造函數體前,中間使用:(colon)
分割。
class Person { String firstName; Person.fromJson(Map data) { print('in Person'); } } class Employee extends Person { // 父類沒有無參數的非命名構造函數,必須手動調用一個構造函數 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'; }
初始化列表
class Point { num x; num y; Point(this.x, this.y); // 初始化列表在構造函數運行前設置實例變量。 Point.fromJson(Map jsonMap) : x = jsonMap['x'], y = jsonMap['y'] { print('In Point.fromJson(): ($x, $y)'); } }
注意:上述代碼,初始化程序沒法訪問 this 關鍵字。
靜態構造函數
class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); static final ImmutablePoint origin = const ImmutablePoint(0, 0); }
重定向構造函數
class Point { num x; num y; // 主構造函數 Point(this.x, this.y) { print("Point($x, $y)"); } // 重定向構造函數,指向主構造函數,函數體爲空 Point.alongXAxis(num x) : this(x, 0); } void main() { var p1 = new Point(1, 2); var p2 = new Point.alongXAxis(4); }
常量構造函數
若是類的對象不會發生變化,能夠構造一個編譯時的常量構造函數。定義格式以下:
class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); static final ImmutablePoint origin = const ImmutablePoint(0, 0); }
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); } } }
注意:工廠構造函數不能用 this。
實例方法
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
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 ,就會把他的值存在臨時變量裏。
抽象方法
abstract class Doer { // ...定義實例變量和方法... void doSomething(); // 定義一個抽象方法。 } class EffectiveDoer extends Doer { void doSomething() { // ...提供一個實現,因此這裏的方法不是抽象的... } }
枚舉類型
枚舉類型,一般被稱爲 enumerations 或 enums ,是一種用來表明一個固定數量的常量的特殊類。
聲明一個枚舉類型須要使用關鍵字 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);
```
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' } ``` ***枚舉類型有如下限制*** * 你不能在子類中混合或實現一個枚舉。 * 你不能顯式實例化一個枚舉。
爲類添加特徵: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; } }
要實現 mixin ,就建立一個繼承 Object 類的子類,不聲明任何構造函數,不調用 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 關鍵字來實現類變量和類方法。
只有當靜態變量被使用時才被初始化。
靜態變量, 靜態變量(類變量)對於類狀態和常數是有用的:
class Color { static const red = const Color('red'); // 一個恆定的靜態變量 final String name; // 一個實例變量。 const Color(this.name); // 一個恆定的構造函數。 } main() { assert(Color.red.name == 'red'); }
靜態方法, 靜態方法(類方法)不在一個實例上進行操做,於是沒必要訪問 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); }
注:考慮到使用高階層的方法而不是靜態方法,是爲了經常使用或者普遍使用的工具和功能。
你能夠將靜態方法做爲編譯時常量。例如,你能夠把靜態方法做爲一個參數傳遞給靜態構造函數。
使用 abstract 修飾符來定義一個抽象類,該類不能被實例化。抽象類在定義接口的時候很是有用,實際上抽象中也包含一些實現。若是你想讓你的抽象類被實例化,請定義一個 工廠構造函數 。
抽象類一般包含 抽象方法。下面是聲明一個含有抽象方法的抽象類的例子:
// 這個類是抽象類,所以不能被實例化。 abstract class AbstractContainer { // ...定義構造函數,域,方法... void updateChildren(); // 抽象方法。 }
下面的類不是抽象類,所以它能夠被實例化,即便定義了一個抽象方法:
class SpecializedContainer extends AbstractContainer { // ...定義更多構造函數,域,方法... void updateChildren() { // ...實現 updateChildren()... } // 抽象方法形成一個警告,可是不會阻止實例化。 void doSomething(); }
每一個類隱式的定義了一個接口,含有類的全部實例和它實現的全部接口。若是你想建立一個支持類 B 的 API 的類 A,但又不想繼承類 B ,那麼,類 A 應該實現類 B 的接口。
一個類實現一個或更多接口經過用 implements 子句聲明,而後提供 API 接口要求。例如:
// 一個 person ,包含 greet() 的隱式接口。 class Person { // 在這個接口中,只有庫中可見。 final _name; // 不在接口中,由於這是個構造函數。 Person(this._name); // 在這個接口中。 String greet(who) => 'Hello, $who. I am $_name.'; } // Person 接口的一個實現。 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())); }
這裏是具體說明一個類實現多個接口的例子:
class Point implements Comparable, Location { // ... }
使用 extends 建立一個子類,同時 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) { // ... } }
@proxy class A { void noSuchMethod(Invocation mirror) { // ... } }
import,part,library指令能夠幫助建立一個模塊化的,可共享的代碼庫。庫不只提供了API,還提供隱私單元:如下劃線(_)開頭的標識符只對內部庫可見。每一個Dartapp就是一個庫,即便它不使用庫指令。
庫能夠分佈式使用包。見 Pub Package and Asset Manager 中有關pub(SDK中的一個包管理器)。
使用庫
使用 import 來指定如何從一個庫命名空間用於其餘庫的範圍。
例如,Dart Web應用通常採用這個庫 dart:html,能夠這樣導入:
import 'dart:html';
import 'dart:io'; import 'package:mylib/mylib.dart'; import 'package:utils/utils.dart';
```
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)加載)容許應用程序按需加載庫。下面是當你可能會使用延遲加載某些狀況:
爲了延遲加載一個庫,你必須使用 deferred as 先導入它。
import 'package:deferred/hello.dart' deferred as hello;
當須要庫時,使用該庫的調用標識符調用 LoadLibrary()。
greet() async { await hello.loadLibrary(); hello.printGreeting(); }
在前面的代碼,在庫加載好以前,await關鍵字都是暫停執行的。有關 async 和 await 見 asynchrony support 的更多信息。
您能夠在一個庫調用 LoadLibrary() 屢次都沒有問題。該庫也只被加載一次。
當您使用延遲加載,請記住如下內容:
庫的實現
聲明庫
利用library identifier(庫標識符)指定當前庫的名稱:
// 聲明庫,名ballgame library ballgame; // 導入html庫 import 'dart:html'; // ...代碼從這裏開始...
關聯文件與庫
添加實現文件,把part fileUri放在有庫的文件,其中fileURI是實現文件的路徑。而後在實現文件中,添加部分標識符(part of identifier),其中標識符是庫的名稱。下面的示例使用的一部分,在三個文件來實現部分庫。
第一個文件,ballgame.dart,聲明球賽庫,導入其餘須要的庫,並指定ball.dart和util.dart是此庫的部分:
library ballgame;
import 'dart:html'; // ...其餘導入在這裏... part 'ball.dart'; part 'util.dart'; // ...代碼從這裏開始...
第二個文件ball.dart,實現了球賽庫的一部分:
part of ballgame; // ...代碼從這裏開始...
第三個文件,util.dart,實現了球賽庫的其他部分:
part of ballgame; // ...Code goes here...
* 能夠經過從新導出部分庫或者所有庫來組合或從新打包庫。例如,你可能有實現爲一組較小的庫集成爲一個較大庫。或者你能夠建立一個庫,提供了從另外一個庫方法的子集。
```
// 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 來表示一個值時,你有兩個選擇。
一樣的,當你須要從 Stream 獲取值的時候,你有兩個選擇。
使用 async 和 await 的代碼是異步的,不過它看起來很像同步的代碼。好比這裏有一段使用 await 等待一個異步函數結果的代碼:
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... }
聲明異步函數
一個異步函數是一個由 async 修飾符標記的函數。雖然一個異步函數可能在操做上比較耗時,可是它能夠當即返回-在任何方法體執行以前。
checkVersion() async { // ... } lookUpVersion() async => /* ... */;
在函數中添加關鍵字 async 使得它返回一個 Future,好比,考慮一下這個同步函數,它將返回一個字符串。
String lookUpVersionSync() => '1.0.0';
若是你想更改它成爲異步方法-由於在之後的實現中將會很是耗時-它的返回值是一個 Future 。
Future<String> lookUpVersion() async => '1.0.0';
請注意函數體不須要使用 Future API,若是必要的話 Dart 將會本身建立 Future 對象
一個 await表達式具備如下形式
await expression
在異步方法中你可使用 await 屢次。好比,下列代碼爲了獲得函數的結果一共等待了三次。
var entrypoint = await findEntrypoint(); var exitCode = await runExecutable(entrypoint, args); await flushThenExit(exitCode);
在 await 表達式
中, 表達式
的值一般是一個 Future 對象;若是不是,那麼這個值會自動轉爲 Future。這個 Future 對象代表了表達式應該返回一個對象。await 表達式
的值就是返回的一個對象。在對象可用以前,await 表達式
將會一直處於暫停狀態。
若是 await 沒有起做用,請確認它是一個異步方法。好比,在你的 main() 函數裏面使用await,main() 的函數體必須被 async 標記:
``` main() async { checkVersion(); print('In main: version is ${await lookUpVersion()}'); } ```
結合 streams 使用異步循環
await for (variable declaration in expression) { // Executes each time the stream emits a value. }
表達式
的值必須有Stream 類型(流類型)。執行過程以下:
若是要中止監聽 stream ,你可使用 break 或者 return 語句,跳出循環並取消來自 stream 的訂閱 。
若是一個異步 for 循環沒有正常運行,請確認它是一個異步方法。 好比,在應用的 main() 方法中使用異步的 for 循環時,main() 的方法體必須被 async 標記。
main() async { ... await for (var request in requestServer) { handleRequest(request); } ... }