[Dart]語法基礎

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公衆號:山青詠芝(shanqingyongzhi)
➤博客園地址:山青詠芝(https://www.cnblogs.com/strengthen/ 
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:http://www.javashuo.com/article/p-xqjwllar-r.html 
➤若是連接不是山青詠芝的博客園地址,則多是爬取做者的文章。
➤原文已修改更新!強烈建議點擊原文地址閱讀!支持做者!支持原創!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★javascript

此頁面向您展現如何使用每一個主要Dart功能,從變量和運算符到類和庫,假設您已經知道如何使用其餘語言編程。html

要了解有關Dart核心庫的更多信息,請參閱 Dart Libraries之旅。不管什麼時候須要有關語言功能的更多詳細信息,請參閱Dart語言規範java

提示: 您可使用DartPad播放Dart的大部分語言功能(瞭解更多信息)。git

打開DartPad程序員

一個基本的Dart項目

如下代碼使用了Dart的許多基本功能:github

 1 // Define a function.
 2 printInteger(int aNumber) {
 3   print('The number is $aNumber.'); // Print to console.
 4 }
 5 
 6 // This is where the app starts executing.
 7 main() {
 8   var number = 42; // Declare and initialize a variable.
 9   printInteger(number); // Call a function.
10 }

如下是此程序使用的適用於全部(或幾乎全部)Dart應用程序的內容:web

// This is a comment.

單行註釋。Dart還支持多行和文檔註釋。有關詳情,請參閱註釋正則表達式

int

一種類型。一些其餘的內置類型 是StringListbool算法

42

一個字面數字。數字文字是一種編譯時常量。express

print()

一種方便的顯示輸出方式。

'...'(或"..."

字符串文字。

$variableName(或)${expression}

字符串插值:包括字符串文字內部的變量或表達式的字符串。有關更多信息,請參閱 字符串

main()

應用程序執行開始的特殊,必需的頂級功能。有關更多信息,請參閱 main()函數

var

一種聲明變量而不指定其類型的方法。

注意: 本網站的代碼遵循Dart風格指南中的約定 。

重要的概念

當您瞭解Dart語言時,請記住如下事實和概念:

  • 您能夠放在變量中的全部內容都是一個對象,每一個對象都是一個類的實例。偶數,函數和 null對象。全部對象都繼承自Object類。

  • 儘管Dart是強類型的,但類型註釋是可選的,由於Dart能夠推斷類型。在上面的代碼中,number 推斷爲類型int。若是要明確說明不須要任何類型,請 使用特殊類型dynamic

  • Dart支持泛型類型,如List<int>(整數列表)或List<dynamic>(任何類型的對象列表)。

  • Dart支持頂級函數(例如main()),以及綁定到類或對象的函數(分別是靜態和實例方法)。您還能夠在函數內建立函數(嵌套函數或本地函數)。

  • 相似地,Dart支持頂級變量,以及綁定到類或對象的變量(靜態和實例變量)。實例變量有時稱爲字段或屬性。

  • 與Java,Dart不具有關鍵字publicprotectedprivate。若是標識符如下劃線(_)開頭,則它對其庫是私有的。有關詳細信息,請參閱 庫和可見性

  • 標識符能夠以字母或下劃線(_)開頭,後跟這些字符加數字的任意組合。

  • Dart有兩個表達式(具備運行時值)和 語句(不具備)。例如,條件表達式 condition ? expr1 : expr2的值爲expr1expr2。將其與if-else語句進行比較,該語句沒有任何值。語句一般包含一個或多個表達式,但表達式不能直接包含語句。

  • Dart工具能夠報告兩種問題:警告和錯誤。警告只是代表您的代碼可能沒法正常工做,但它們不會阻止您的程序執行。錯誤能夠是編譯時或運行時。編譯時錯誤會阻止代碼執行; 運行時錯誤致使 代碼執行時引起異常

關鍵詞

下表列出了Dart語言專門處理的單詞。

abstract 2 dynamic 2 implements 2 show 1
as 2 else import 2 static 2
assert enum in super
async 1 export 2 interface 2 switch
await 3 extends is sync 1
break external 2 library 2 this
case factory 2 mixin 2 throw
catch false new true
class final null try
const finally on 1 typedef 2
continue for operator 2 var
covariant 2 Function 2 part 2 void
default get 2 rethrow while
deferred 2 hide 1 return with
do if set 2 yield 3

 

  • 帶有上標1的單詞是上下文關鍵字,僅在特定位置具備含義。它們無處不在是有效的標識符。避免使用這些單詞做爲標識符。可是,若有必要,標有上標的關鍵字能夠是標識符:
  • 帶有上標2的單詞是內置標識符。爲了簡化將JavaScript代碼移植到Dart的任務,這些關鍵字在大多數地方都是有效的標識符,但它們不能用做類或類型名稱,也不能用做導入前綴。

  • 帶有上標3的單詞是與Dart 1.0發佈後添加的異步支持相關的更新,有限的保留字。不能使用awaityield做爲任何函數體中的標識符標記asyncasync*sync*

表中的全部其餘單詞都是保留字,不能是標識符。

變量

這是建立變量並初始化它的示例:

var name = 'Bob';

變量存儲引用。調用的變量name包含對String值爲「Bob」 的對象的引用。

name推斷變量的類型String,但您能夠經過指定它來更改該類型。若是對象不限於單一類型,請按照設計準則指定Objectdynamic類型 。

dynamic name = 'Bob';

另外一種選擇是顯式聲明將推斷出的類型:

String name = 'Bob';

注意: 此頁面遵循 樣式指南建議 使用var,而不是鍵入註釋,用於局部變量。

默認值

未初始化的變量的初始值爲null。即便是具備數字類型的變量最初也是null,由於數字 - 就像Dart中的其餘全部 - 都是對象。

1 int lineCount;
2 assert(lineCount == null);

注意:assert()生產代碼中將忽略 該調用。在開發期間, 除非條件爲真,不然拋出異常。有關詳細信息,請參閱斷言assert(condition)

常量:Final and const

若是您從未打算更改變量,請使用finalconst代替var或替代類型。最終變量只能設置一次; const變量是編譯時常量。(Const變量是隱式最終的。)最終的頂級或類變量在第一次使用時被初始化。

注意: 實例變量能夠是final但不是const。必須在構造函數體啓動以前初始化最終實例變量 - 在變量聲明,構造函數參數或構造函數的初始化列表中

如下是建立和設置最終變量的示例:

1 final name = 'Bob'; // Without a type annotation
2 final String nickname = 'Bobby';

您沒法更改最終變量的值:

name = 'Alice'; // Error: a final variable can only be set once.

使用const爲您要爲變量的編譯時間常數。若是const變量在類級別,請標記它static const。在聲明變量的地方,將值設置爲編譯時常量,例如數字或字符串文字,const變量或對常數進行算術運算的結果:

1 const bar = 1000000; // Unit of pressure (dynes/cm2)
2 const double atm = 1.01325 * bar; // Standard atmosphere

const關鍵字不僅是聲明常數變量。您還可使用它來建立常量值,以及聲明建立常量值的構造函數。任何變量均可以具備常量值。

1 var foo = const [];
2 final bar = const [];
3 const baz = []; // Equivalent to `const []`

您能夠省略聲明const的初始化表達式,如上所述。有關詳細信息,請參閱不要冗餘地使用constconstbaz

您能夠更改非final,非const變量的值,即便它曾經有一個const值:

foo = [1, 2, 3]; // Was const []

您沒法更改const變量的值:

baz = [42]; // Error: Constant variables can't be assigned a value.

有關使用const建立常量值的更多信息,請參閱 列表Hash集合

內置類型

Dart語言特別支持如下類型:

  • 數字
  • 字符串
  • 布爾
  • 列表(也稱爲數組)
  • Hash集合
  • 符文(用於表示字符串中的Unicode字符)
  • 符號

您可使用文字初始化任何這些特殊類型的對象。例如,'this is a string'是一個字符串文字,true是一個布爾文字。

由於Dart中的每一個變量都引用一個對象 - 一個類的實例 - 您一般可使用構造函數來初始化變量。一些內置類型有本身的構造函數。例如,您可使用Map()構造函數來建立Hash集合。

數字

Dart號有兩種形式:

int

整數值不大於64位,具體取決於平臺。在Dart VM上,值能夠是-2 63到2 63 - 1.編譯爲JavaScript的Dart使用JavaScript編號, 容許從-2 53到2 53 - 1的值。

double

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

這兩個intdouble的亞型num 號碼類型包括基本的運算符,如+, - ,/和*,也是在那裏你會發現abs()ceil()floor()其餘方法中。(按位運算符,例如>>,在int類中定義。)若是num及其子類型沒有您要查找的內容,則 dart:math庫可能會。

整數是沒有小數點的數字。如下是定義整數文字的一些示例:

1 var x = 1;
2 var hex = 0xDEADBEEF;

若是數字包含小數,則爲雙精度數。如下是定義雙重文字的一些示例:

1 var y = 1.1;
2 var exponents = 1.42e5;

從Dart 2.1開始,必要時整數文字會自動轉換爲雙精度數:

double z = 1; // Equivalent to double z = 1.0.

版本說明: 在Dart 2.1以前,在雙上下文中使用整數文字是錯誤的。

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

 1 // String -> int
 2 var one = int.parse('1');
 3 assert(one == 1);
 4 
 5 // String -> double
 6 var onePointOne = double.parse('1.1');
 7 assert(onePointOne == 1.1);
 8 
 9 // int -> String
10 String oneAsString = 1.toString();
11 assert(oneAsString == '1');
12 
13 // double -> String
14 String piAsString = 3.14159.toStringAsFixed(2);
15 assert(piAsString == '3.14');

int類型指定傳統的按位移位(<<,>>),AND(&)和OR(|)運算符。例如:

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

文字數字是編譯時常量。許多算術表達式也是編譯時常量,只要它們的操做數是編譯爲數字的編譯時常量。

1 const msPerSecond = 1000;
2 const secondsUntilRetry = 5;
3 const msUntilRetry = secondsUntilRetry * msPerSecond;

字符串

Dart字符串是一系列UTF-16代碼單元。您可使用單引號或雙引號來建立字符串:

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

您可使用表達式將表達式的值放在字符串中 。若是表達式是標識符,則能夠跳過{}。要獲取與對象相對應的字符串,Dart會調用該對象的方法。${expression}.

1 var s = 'string interpolation';
2 
3 assert('Dart has $s, which is very handy.' ==
4     'Dart has string interpolation, ' +
5         'which is very handy.');
6 assert('That deserves all caps. ' +
7         '${s.toUpperCase()} is very handy!' ==
8     'That deserves all caps. ' +
9         'STRING INTERPOLATION is very handy!');

注: 在==兩個物體操做測試是不是等價的。若是兩個字符串包含相同的代碼單元序列,則它們是等效的。

您可使用相鄰的字符串文字或+ 運算符來鏈接字符串:

1 var s1 = 'String '
2     'concatenation'
3     " works even over line breaks.";
4 assert(s1 ==
5     'String concatenation works even over '
6     'line breaks.');
7 
8 var s2 = 'The + operator ' + 'works, as well.';
9 assert(s2 == 'The + operator works, as well.');

建立多行字符串的另外一種方法:使用帶有單引號或雙引號的三引號:

1 var s1 = '''
2 You can create
3 multi-line strings like this one.
4 ''';
5 
6 var s2 = """This is also a
7 multi-line string.""";

您能夠經過爲其添加前綴來建立「原始」字符串r

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

有關如何在字符串中表示Unicode字符的詳細信息,請參閱Runes

文字字符串是編譯時常量,只要任何插值表達式是一個編譯時常量,其值爲null或數值,字符串或布爾值。

 1 // These work in a const string.
 2 const aConstNum = 0;
 3 const aConstBool = true;
 4 const aConstString = 'a constant string';
 5 
 6 // These do NOT work in a const string.
 7 var aNum = 0;
 8 var aBool = true;
 9 var aString = 'a string';
10 const aConstList = [1, 2, 3];
11 
12 const validConstString = '$aConstNum $aConstBool $aConstString';
13 // const invalidConstString = '$aNum $aBool $aString $aConstList';

有關使用字符串的更多信息,請參閱 字符串和正則表達式

布爾

爲了表示布爾值,Dart具備一個名爲的類型bool。只有兩個對象具備bool類型:boolean literals truefalse,它們都是編譯時常量。

Dart的類型安全意味着你不能使用像或 那樣的代碼 。相反,明確檢查值,以下所示:if (nonbooleanValue)assert (nonbooleanValue)

 1 // Check for an empty string.
 2 var fullName = '';
 3 assert(fullName.isEmpty);
 4 
 5 // Check for zero.
 6 var hitPoints = 0;
 7 assert(hitPoints <= 0);
 8 
 9 // Check for null.
10 var unicorn;
11 assert(unicorn == null);
12 
13 // Check for NaN.
14 var iMeantToDoThis = 0 / 0;
15 assert(iMeantToDoThis.isNaN);

Lists

也許幾乎每種編程語言中最多見的集合是數組或有序的對象組。在Dart中,數組是 List對象,所以大多數人只是將它們稱爲列表。

Dart列表文字看起來像JavaScript數組文字。這是一個簡單的Dart列表:

var list = [1, 2, 3];

注意: Dart推斷出list有類型List<int>。若是嘗試將非整數對象添加到此列表,則分析器或運行時會引起錯誤。有關更多信息,請閱讀 類型推斷。

列表使用從零開始的索引,其中0是第一個元素list.length - 1的索引,而且是最後一個元素的索引。您能夠得到列表的長度並像在JavaScript中同樣引用列表元素:

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

要建立一個編譯時常量const的列表,請在列表文字前添加:

1 var constantList = const [1, 2, 3];
2 // constantList[1] = 1; // Uncommenting this causes an error.

List類型有許多方便的方法來操做列表。有關列表的更多信息,請參閱泛型和 集合

集:Sets

Dart中的一組是一組無序的獨特物品。對集合的Dart支持由set literals和Set類型提供。

版本說明:儘管Set 類型一直是Dart的核心部分,但在Dart 2.2中引入了set literals。

這是一個簡單的Dart集,使用set literal建立:

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

注意: Dart推斷出halogens具備該類型 Set<String>。若是您嘗試向集合中添加錯誤類型的值,則分析器或運行時會引起錯誤。有關更多信息,請閱讀 類型推斷。

要建立一個空集,請使用{}前面帶有類型參數,或者指定{}給類型的變量Set

1 var names = <String>{};
2 // Set<String> names = {}; // This works, too.
3 // var names = {}; // Creates a map, not a set.

設置仍是映射? 映射文字的語法相似於集合文字的語法。因爲Hash集合文字是第一個,所以{}默認爲該Map類型。若是您忘記了類型註釋{}或它所分配的變量,則Dart會建立一個類型的對象Map<dynamic, dynamic>

使用add()addAll()方法將項添加到現有集:

1 var elements = <String>{};
2 elements.add('fluorine');
3 elements.addAll(halogens);

使用.length獲得的一組項目的數量:

1 var elements = <String>{};
2 elements.add('fluorine');
3 elements.addAll(halogens);
4 assert(elements.length == 5);

要建立一個編譯時常量const的集合,請在set literal以前添加:

1 final constantSet = const {
2   'fluorine',
3   'chlorine',
4   'bromine',
5   'iodine',
6   'astatine',
7 };
8 // constantSet.add('helium'); // Uncommenting this causes an error.

有關集的更多信息,請參閱 泛型和 

Hash集合:Maps

一般,映射是關聯鍵和值的對象。鍵和值均可以是任何類型的對象。每一個鍵只出現一次,但您能夠屢次使用相同的值。Hash集合的Dart支持由Hash集合文字和Map類型提供。

這裏有幾個簡單的Dart貼圖,使用貼圖文字建立:

 1 var gifts = {
 2   // Key:    Value
 3   'first': 'partridge',
 4   'second': 'turtledoves',
 5   'fifth': 'golden rings'
 6 };
 7 
 8 var nobleGases = {
 9   2: 'helium',
10   10: 'neon',
11   18: 'argon',
12 };

注意: Dart推斷出gifts具備類型 Map<String, String>nobleGases類型的 Dart Map<int, String>。若是您嘗試將錯誤類型的值添加到任一映射,則分析器或運行時會引起錯誤。有關更多信息,請閱讀 類型推斷。

您可使用Map構造函數建立相同的對象:

1 var gifts = Map();
2 gifts['first'] = 'partridge';
3 gifts['second'] = 'turtledoves';
4 gifts['fifth'] = 'golden rings';
5 
6 var nobleGases = Map();
7 nobleGases[2] = 'helium';
8 nobleGases[10] = 'neon';
9 nobleGases[18] = 'argon';

注意: 您可能但願看到new Map()而不只僅是Map()。從Dart 2開始,new關鍵字是可選的。有關詳細信息,請參閱使用構造函數

像在JavaScript中同樣,將新的鍵值對添加到現有Hash集合中:

1 var gifts = {'first': 'partridge'};
2 gifts['fourth'] = 'calling birds'; // Add a key-value pair

以與在JavaScript中相同的方式從Hash集合中檢索值:

1 var gifts = {'first': 'partridge'};
2 assert(gifts['first'] == 'partridge');

若是您查找不在Hash集合中的鍵,則會獲得null做爲回報:

var gifts = { 'first' 'partridge' }; 斷言gifts [ 'fifth' ] == null );

使用.length獲得的映射中的鍵值對的數量:

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

要建立一個編譯時常量const的Hash集合,請在Hash集合文字以前添加:

1 final constantMap = const {
2   2: 'helium',
3   10: 'neon',
4   18: 'argon',
5 };
6 
7 // constantMap[2] = 'Helium'; // Uncommenting this causes an error.

有關Hash集合的更多信息,請參閱 泛型和 Hash集合

符文:Runes

在Dart中,符文是字符串的UTF-32代碼點。

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

表達Unicode代碼點的經常使用方法是 \uXXXX,XXXX是4位十六進制值。例如,心臟角色(♥)是\u2665。要指定多於或少於4個十六進制數字,請將值放在大括號中。例如,笑的表情符號(😆)是\u{1f600}

字符串 類有幾個屬性,你能夠用它來提取符文信息。在codeUnitAtcodeUnit屬性返回16位編碼單元。使用該runes屬性獲取字符串的符文。

如下示例說明了符文,16位代碼單元和32位代碼點之間的關係。單擊「運行」按鈕 以查看運行中的符文。

注意: 使用列表操做操做符文時要當心。這種方法很容易分解,具體取決於特定的語言,字符集和操做。有關更多信息,請參閱 如何在Dart中反轉字符串?在Stack Overflow上。

符號

符號對象表示達特程序聲明的操做者或標識符。您可能永遠不須要使用符號,但它們對於按名稱引用標識符的API很是有用,由於縮小會更改標識符名稱而不會更改標識符符號。

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

1 #radix
2 #bar

符號文字是編譯時常量。 

函數

Dart是一種真正的面嚮對象語言,所以即便是函數也是對象而且具備類型Function。 這意味着函數能夠分配給變量或做爲參數傳遞給其餘函數。您也能夠調用Dart類的實例,就好像它是一個函數同樣。有關詳細信息,請參閱可調用類

如下是實現函數的示例:

1 bool isNoble(int atomicNumber) {
2   return _nobleGases[atomicNumber] != null;
3 }

雖然Effective Dart建議 爲公共API鍵入註釋,但若是省略類型,該函數仍然有效:

1 isNoble(atomicNumber) {
2   return _nobleGases[atomicNumber] != null;
3 }

對於只包含一個表達式的函數,可使用簡寫語法:

1 bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

該語法是一個速記 。有時將表示法稱爲箭頭語法。=> expr{ return expr; }=>

注意: 只有表達式 - 不是語句 - 能夠出如今箭頭(=>)和分號(;)之間。例如,您不能在其中放置if語句,但可使用條件表達式

函數能夠有兩種類型的參數:必需和可選。首先列出所需參數,而後列出任何可選參數。命名的可選參數也能夠標記爲@required。有關詳細信息,請參閱下一節。

可選參數

可選參數能夠是位置參數,也能夠是命名參數,但不能同時包

可選的命名參數

調用函數時,可使用指定命名參數 。例如:paramName: value

enableFlags(bold: true, hidden: false);

定義函數時,用於 指定命名參數:{param1, param2, …}

1 /// Sets the [bold] and [hidden] flags ...
2 void enableFlags({bool bold, bool hidden}) {...}

Flutter實例建立表達式可能變得複雜,所以窗口小部件構造函數僅使用命名參數。這使得實例建立表達式更易於閱讀。

您可使用@required在任何Dart代碼(不只僅是Flutter)中註釋命名參數, 以指示它是必需參數。例如:

const Scrollbar({Key key, @required Widget child})

當一個Scrollbar構造,分析儀時,報告一個問題 child的說法是不存在的。

必需包中定義。能夠package:meta/meta.dart直接導入 ,也能夠導入另外一個導出的包 meta,例如Flutter package:flutter/material.dart

可選的位置參數

包裝一組函數參數將[]它們標記爲可選的位置參數:

1 String say(String from, String msg, [String device]) {
2   var result = '$from says $msg';
3   if (device != null) {
4     result = '$result with a $device';
5   }
6   return result;
7 }

這是一個在沒有可選參數的狀況下調用此函數的示例:

assert(say('Bob', 'Howdy') == 'Bob says Howdy');

如下是使用第三個參數調用此函數的示例:

1 assert(say('Bob', 'Howdy', 'smoke signal') ==
2     'Bob says Howdy with a smoke signal'); 

默認參數值

您的函數可用於=定義命名和位置參數的默認值。默認值必須是編譯時常量。若是未提供默認值,則默認值爲null

如下是爲命名參數設置默認值的示例:

1 /// Sets the [bold] and [hidden] flags ...
2 void enableFlags({bool bold = false, bool hidden = false}) {...}
3 
4 // bold will be true; hidden will be false.
5 enableFlags(bold: true);

棄用註釋: 舊代碼可能使用冒號(:)而不是= 設置命名參數的默認值。緣由是最初只:支持命名參數。該支持可能已被棄用,所以咱們建議您 使用=指定默認值。

下一個示例顯示如何設置位置參數的默認值:

 1 String say(String from, String msg,
 2     [String device = 'carrier pigeon', String mood]) {
 3   var result = '$from says $msg';
 4   if (device != null) {
 5     result = '$result with a $device';
 6   }
 7   if (mood != null) {
 8     result = '$result (in a $mood mood)';
 9   }
10   return result;
11 }
12 
13 assert(say('Bob', 'Howdy') ==
14     'Bob says Howdy with a carrier pigeon');

您還能夠將列表或Hash集合做爲默認值傳遞。如下示例定義了一個函數,該函數doStuff()指定參數的默認列表和list 參數的默認映射gifts

 1 void doStuff(
 2     {List<int> list = const [1, 2, 3],
 3     Map<String, String> gifts = const {
 4       'first': 'paper',
 5       'second': 'cotton',
 6       'third': 'leather'
 7     }}) {
 8   print('list:  $list');
 9   print('gifts: $gifts');
10 }

main()函數

每一個應用程序都必須具備頂級main()功能,該功能用做應用程序的入口點。該main()函數返回void並具備List<String>參數的可選參數。

如下main()是Web應用程序功能的示例:

1 void main() {
2   querySelector('#sample_text_id')
3     ..text = 'Click me!'
4     ..onClick.listen(reverseText);
5 }

注意:..前面代碼中 的語法稱爲級聯。使用級聯,您能夠對單個對象的成員執行多個操做。

如下main()是帶參數的命令行應用程序的函數示例:

1 // Run the app like this: dart args.dart 1 test
2 void main(List<String> arguments) {
3   print(arguments);
4 
5   assert(arguments.length == 2);
6   assert(int.parse(arguments[0]) == 1);
7   assert(arguments[1] == 'test');
8 }

您可使用args庫來定義和解析命令行參數。

做爲第一類對象的功能

您能夠將函數做爲參數傳遞給另外一個函數。例如:

1 void printElement(int element) {
2   print(element);
3 }
4 
5 var list = [1, 2, 3];
6 
7 // Pass printElement as a parameter.
8 list.forEach(printElement);

您還能夠爲變量分配函數,例如:

1 var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
2 assert(loudify('hello') == '!!! HELLO !!!');

此示例使用匿名函數。更多關於下一節的內容。

匿名函數

大多數函數都被命名,例如main()printElement()。您還能夠建立一個名爲匿名函數的無名函數,有時也能夠建立一個lambda或閉包。您能夠爲變量分配匿名函數,以便例如能夠在集合中添加或刪除它。

匿名函數看起來相似於命名函數 - 零個或多個參數,在逗號和括號之間用逗號和可選類型註釋分隔。

後面的代碼塊包含函數的主體:

1 ([[Type] param1[, …]]) { 
2   codeBlock; 
3 }; 

如下示例使用無類型參數定義匿名函數item。爲列表中的每一個項調用的函數將打印一個包含指定索引處的值的字符串。

1 var list = ['apples', 'bananas', 'oranges'];
2 list.forEach((item) {
3   print('${list.indexOf(item)}: $item');
4 });

單擊運行按鈕以執行代碼。

若是函數只包含一個語句,則可使用箭頭表示法縮短它。將如下行粘貼到DartPad中,而後單擊「運行」以驗證它是否在功能上等效。

1 list.forEach(
2     (item) => print('${list.indexOf(item)}: $item'));

詞彙範圍

Dart是一種詞法範圍的語言,這意味着變量的範圍是靜態肯定的,只需經過代碼的佈局。您能夠「向外跟隨花括號」以查看變量是否在範圍內。

如下是每一個範圍級別包含變量的嵌套函數示例:

 1 bool topLevel = true;
 2 
 3 void main() {
 4   var insideMain = true;
 5 
 6   void myFunction() {
 7     var insideFunction = true;
 8 
 9     void nestedFunction() {
10       var insideNestedFunction = true;
11 
12       assert(topLevel);
13       assert(insideMain);
14       assert(insideFunction);
15       assert(insideNestedFunction);
16     }
17   }
18 }

請注意如何nestedFunction()使用每一個級別的變量,一直到頂級。

詞彙封閉

甲閉合是可以訪問在其詞法範圍的變量的函數的對象,即便當函數用於其原來的範圍以外。

函數能夠關閉周圍範圍中定義的變量。在如下示例中,makeAdder()捕獲變量addBy。不管返回的函數在哪裏,它都會記住addBy

 1 /// Returns a function that adds [addBy] to the
 2 /// function's argument.
 3 Function makeAdder(num addBy) {
 4   return (num i) => addBy + i;
 5 }
 6 
 7 void main() {
 8   // Create a function that adds 2.
 9   var add2 = makeAdder(2);
10 
11   // Create a function that adds 4.
12   var add4 = makeAdder(4);
13 
14   assert(add2(3) == 5);
15   assert(add4(3) == 7);

測試函數是否相等

如下是測試頂級函數,靜態方法和實例方法的相等性的示例:

 1 void foo() {} // A top-level function
 2 
 3 class A {
 4   static void bar() {} // A static method
 5   void baz() {} // An instance method
 6 }
 7 
 8 void main() {
 9   var x;
10 
11   // Comparing top-level functions.
12   x = foo;
13   assert(foo == x);
14 
15   // Comparing static methods.
16   x = A.bar;
17   assert(A.bar == x);
18 
19   // Comparing instance methods.
20   var v = A(); // Instance #1 of A
21   var w = A(); // Instance #2 of A
22   var y = w;
23   x = w.baz;
24 
25   // These closures refer to the same instance (#2),
26   // so they're equal.
27   assert(y.baz == x);
28 
29   // These closures refer to different instances,
30   // so they're unequal.
31   assert(v.baz != w.baz);
32 }

返回值

全部函數都返回一個值。若是未指定返回值,則將語句return null;隱式附加到函數體。

1 foo() {}
2 
3 assert(foo() == null);

運算符

Dart定義下表中顯示的運算符。您能夠覆蓋許多運營商,如在 可重寫運營商

描述 操做者
一元后綴 expr++    expr--    ()    []    .    ?.
一元前綴 -expr    !expr    ~expr    ++expr    --expr   
*    /    %  ~/
添加劑 +    -
轉移 <<    >>    >>>
按位AND &
按位異或 ^
按位OR |
關係和類型測試 >=    >    <=    <    as    is    is!
平等 ==    !=   
邏輯AND &&
邏輯或 ||
若是爲null ??
有條件的 expr1 ? expr2 : expr3
級聯 ..
分配 =    *=    /=   +=   -=   &=   ^=   等等

警告: 運算符優先級是Dart解析器行爲的近似值。有關明確的答案,請參閱Dart語言規範中的語法 。

使用運算符時,能夠建立表達式。如下是運算符表達式的一些示例:

1 a++
2 a + b
3 a = b
4 a == b
5 c ? a : b
6 a is T

運算符表中,每一個運算符的優先級高於其後的行中的運算符。例如,乘法運算符的%優先級高於(所以以前執行)等於運算符==,它的優先級高於邏輯AND運算符&&。該優先級意味着如下兩行代碼執行相同的方式:

1 // Parentheses improve readability.
2 if ((n % i == 0) && (d % i == 0)) ...
3 
4 // Harder to read, but equivalent.
5 if (n % i == 0 && d % i == 0) ...

警告: 對於處理兩個操做數的運算符,最左邊的操做數肯定使用哪一個版本的運算符。例如,若是您有Vector對象和Point對象,則aVector + aPoint使用Vector版本的+。

算術運算符

Dart支持一般的算術運算符,以下表所示。

操做者 含義
+
減去
-expr 一元減號,也稱爲否認(反轉表達式的符號)
*
/ 劃分
~/ 除以,返回整數結果
% 獲取整數除法的餘數(模數)

例:

1 assert(2 + 3 == 5);
2 assert(2 - 3 == -1);
3 assert(2 * 3 == 6);
4 assert(5 / 2 == 2.5); // Result is a double
5 assert(5 ~/ 2 == 2); // Result is an int
6 assert(5 % 2 == 1); // Remainder
7 
8 assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');

Dart還支持前綴和後綴增量和減量運算符。

操做者 含義
++var var = var + 1(表達式值是var + 1
var++ var = var + 1(表達式值是var
--var var = var – 1(表達式值是var – 1
var-- var = var – 1(表達式值是var

例:

 1 var a, b;
 2 
 3 a = 0;
 4 b = ++a; // Increment a before b gets its value.
 5 assert(a == b); // 1 == 1
 6 
 7 a = 0;
 8 b = a++; // Increment a AFTER b gets its value.
 9 assert(a != b); // 1 != 0
10 
11 a = 0;
12 b = --a; // Decrement a before b gets its value.
13 assert(a == b); // -1 == -1
14 
15 a = 0;
16 b = a--; // Decrement a AFTER b gets its value.
17 assert(a != b); // -1 != 0

平等和關係運營商

下表列出了相等運算符和關係運算符的含義。

操做者 含義
== 等於; 見下面的討論
!= 不相等
> 比...更棒
< 少於
>= 大於或等於
<= 小於或等於

要測試兩個對象x和y是否表示相同的事物,請使用 ==運算符。(在極少數狀況下,您須要知道兩個對象是不是徹底相同的對象,請使用相同的() 函數。)如下是==運算符的工做方式:

  1. 若是x或y爲null,則若是二者都爲null則返回true;若是隻有一個爲null,則返回false。

  2. 返回方法調用的結果 。(這是正確的,運算符,例如在第一個操做數上調用的方法。您甚至能夠覆蓋許多運算符,包括,正如您在Overridable運算符中看到的那樣 。)x.==(y)====

這是使用每一個相等和關係運算符的示例:

1 assert(2 == 2);
2 assert(2 != 3);
3 assert(3 > 2);
4 assert(2 < 3);
5 assert(3 >= 3);
6 assert(2 <= 3);

鍵入測試運算符

使用asisis!運算符能夠方便地在運行時檢查類型。

操做者 含義
as Typecast(也用於指定庫前綴
is 若是對象具備指定的類型,則爲True
is! 若是對象具備指定的類型,則返回false

obj is T若是obj實現了指定的接口,則結果爲true T。例如,obj is Object老是如此。

使用as運算符將對象強制轉換爲特定類型。一般,您應該使用它做爲is對使用該對象的表達式後跟對象的測試的簡寫。例如,請考慮如下代碼:

1 if (emp is Person) {
2   // Type check
3   emp.firstName = 'Bob';
4 }

您可使用as運算符縮短代碼:

(emp as Person).firstName = 'Bob';

注意: 代碼不相同。若是emp爲null或不是Person,則第一個示例(with is)不執行任何操做; 第二個(帶as)拋出一個異常。

分配運營商

如您所見,您可使用=運算符分配值。要僅在assign-to變量爲null時分配,請使用??=運算符。

1 // Assign value to a
2 a = value;
3 // Assign value to b if b is null; otherwise, b stays the same
4 b ??= value;

複合賦值運算符,例如+=將操做與賦值組合在一塊兒。

= –= /= %= >>= ^=
+= *= ~/= <<= &= |=

如下是複合賦值運算符的工做原理:

  複合賦值 等價表達
對於運營商op: a op= b a = a op b
例: a += b a = a + b

如下示例使用賦值和複合賦值運算符:

1 var a = 2; // Assign using =
2 a *= 3; // Assign and multiply: a = a * 3
3 assert(a == 6);

邏輯運算符

您可使用邏輯運算符反轉或組合布爾表達式。

操做者 含義
!expr 反轉如下表達式(將false更改成true,反之亦然)
|| 邏輯或
&& 邏輯AND

如下是使用邏輯運算符的示例:

1 if (!done && (col == 0 || col == 3)) {
2   // ...Do something...
3 }

按位和移位運算符

您能夠在Dart中操縱數字的各個位。一般,您將使用這些按位和移位運算符和整數。

操做者 含義
&
| 要麼
^ XOR
~expr 一元逐位補碼(0s變爲1s; 1s變爲0s)
<< 向左轉
>> 向右轉

這是使用按位和移位運算符的示例:

1 final value = 0x22;
2 final bitmask = 0x0f;
3 
4 assert((value & bitmask) == 0x02); // AND
5 assert((value & ~bitmask) == 0x20); // AND NOT
6 assert((value | bitmask) == 0x2f); // OR
7 assert((value ^ bitmask) == 0x2d); // XOR
8 assert((value << 4) == 0x220); // Shift left
9 assert((value >> 4) == 0x02); // Shift right

條件表達式

Dart有兩個運算符,可讓您簡明地計算可能須要if-else語句的表達式:

condition ? expr1 : expr2
若是condition爲true,則計算expr1(並返回其值); 不然,計算並返回expr2的值。
expr1 ?? expr2
若是expr1爲非null,則返回其值; 不然,計算並返回expr2的值。

當您須要根據布爾表達式分配值時,請考慮使用?:

var visibility = isPublic ? 'public' : 'private';

若是布爾表達式測試爲null,請考慮使用??

String playerName(String name) => name ?? 'Guest';

前面的例子至少能夠用其餘兩種方式編寫,但不能簡潔:

 1 // Slightly longer version uses ?: operator.
 2 String playerName(String name) => name != null ? name : 'Guest';
 3 
 4 // Very long version uses if-else statement.
 5 String playerName(String name) {
 6   if (name != null) {
 7     return name;
 8   } else {
 9     return 'Guest';
10   }
11 }

級聯符號(..)

Cascades(..)容許您對同一對象進行一系列操做。除了函數調用,您還能夠訪問同一對象上的字段。這一般能夠爲您節省建立臨時變量的步驟,並容許您編寫更多流暢的代碼。

請考慮如下代碼:

1 querySelector('#confirm') // Get an object.
2   ..text = 'Confirm' // Use its members.
3   ..classes.add('important')
4   ..onClick.listen((e) => window.alert('Confirmed!'));

第一個方法調用,querySelector()返回一個選擇器對象。級聯表示法後面的代碼對此選擇器對象進行操做,忽略可能返回的任何後續值。

前面的例子至關於:

1 var button = querySelector('#confirm');
2 button.text = 'Confirm';
3 button.classes.add('important');
4 button.onClick.listen((e) => window.alert('Confirmed!'));

您也能夠嵌套您的級聯。例如:

1 final addressBook = (AddressBookBuilder()
2       ..name = 'jenny'
3       ..email = 'jenny@example.com'
4       ..phone = (PhoneNumberBuilder()
5             ..number = '415-555-0100'
6             ..label = 'home')
7           .build())
8     .build();

當心在返回實際對象的函數上構造級聯。例如,如下代碼失敗:

1 var sb = StringBuffer();
2 sb.write('foo')
3   ..write('bar'); // Error: method 'write' isn't defined for 'void'.

sb.write()調用返回無效的,並不能構造上的級聯void

注意: 嚴格來講,級聯的「雙點」符號不是運算符。它只是Dart語法的一部分。

其餘運營商

在其餘示例中,您已經看到了大多數剩餘的運算符:

操做者 名稱 含義
() 功能應用 表示函數調用
[] 列表訪問 引用列表中指定索引處的值
. 會員訪問權限 指表達式的屬性; 示例:從表達式中foo.bar選擇屬性barfoo
?. 有條件的成員訪問權限 好比.,但最左邊的操做數能夠爲null; 示例:從表達式中foo?.bar選擇屬性barfoo除非foo爲null(在這種狀況下,值爲foo?.barnull)

有關詳細信息.?.以及..運營商,見 

控制流程語句

您可使用如下任一方法控制Dart代碼的流程:

  • if 和 else
  • for 循環
  • whiledowhile循環
  • break 和 continue
  • switch 和 case
  • assert

您還可使用try-catch和影響控制流throw,如異常中所述

若是是,不然

Dart支持if帶有可選else語句的語句,以下一個示例所示。另見條件表達式

1 if (isRaining()) {
2   you.bringRainCoat();
3 } else if (isSnowing()) {
4   you.wearJacket();
5 } else {
6   car.putTopDown();
7 }

與JavaScript不一樣,條件必須使用布爾值,沒有別的。有關更多信息,請參閱 布爾值。

For循環

您可使用標準for循環進行迭代。例如:

1 var message = StringBuffer('Dart is fun');
2 for (var i = 0; i < 5; i++) {
3   message.write('!');
4 }

Dart for循環內部的閉包捕獲了索引的值,避免了JavaScript中常見的陷阱。例如,考慮:

1 var callbacks = [];
2 for (var i = 0; i < 2; i++) {
3   callbacks.add(() => print(i));
4 }
5 callbacks.forEach((c) => c());

0而後1,正如預期的那樣輸出。相反,該示例將打印2,而後2在JavaScript中。

若是要迭代的對象是Iterable,則可使用 forEach()方法。forEach()若是您不須要知道當前的迭代計數器,則使用是一個不錯的選擇:

candidates.forEach((candidate) => candidate.interview());

像List和Set這樣for-in的可迭代類也支持迭代的形式 :

1 var collection = [0, 1, 2];
2 for (var x in collection) {
3   print(x); // 0 1 2
4 }

while 和 do-while

一個while循環計算循環前的狀態:

1 while (!isDone()) {
2   doSomething();
3 }

dowhile循環評估循環後的條件:

1 do {
2   printLine();
3 } while (!atEndOfPage());

break 和 continue

使用break中止循環:

1 while (true) {
2   if (shutDownRequested()) break;
3   processIncomingRequests();
4 }

使用continue跳到下一個循環迭代:

1 for (int i = 0; i < candidates.length; i++) {
2   var candidate = candidates[i];
3   if (candidate.yearsExperience < 5) {
4     continue;
5   }
6   candidate.interview();
7 }

若是您使用Iterable(如列表或集合),則可能會以不一樣的方式編寫該示例 :

1 candidates
2     .where((c) => c.yearsExperience >= 5)
3     .forEach((c) => c.interview());

siwtch 和 case

Dart中的switch語句使用比較整數,字符串或編譯時常量==。比較對象必須都是同一個類的實例(而不是其任何子類型),而且該類不能覆蓋==。 枚舉類型switch語句中運行良好。

注意: Dart中的Switch語句適用於有限的狀況,例如解釋器或掃描儀。

每一個非空case子句break一般以語句結束。其餘有效的方式來結束一個非空的case條款是continue, throwreturn聲明。

default當沒有case子句匹配時,使用子句執行代碼:

 1 var command = 'OPEN';
 2 switch (command) {
 3   case 'CLOSED':
 4     executeClosed();
 5     break;
 6   case 'PENDING':
 7     executePending();
 8     break;
 9   case 'APPROVED':
10     executeApproved();
11     break;
12   case 'DENIED':
13     executeDenied();
14     break;
15   case 'OPEN':
16     executeOpen();
17     break;
18   default:
19     executeUnknown();
20 }

如下示例省略breakcase子句中的語句,從而生成錯誤:

 1 var command = 'OPEN';
 2 switch (command) {
 3   case 'OPEN':
 4     executeOpen();
 5     // ERROR: Missing break
 6 
 7   case 'CLOSED':
 8     executeClosed();
 9     break;
10 }

可是,Dart確實支持空case句子,容許一種形式的落空:

1 var command = 'CLOSED';
2 switch (command) {
3   case 'CLOSED': // Empty case falls through.
4   case 'NOW_CLOSED':
5     // Runs for both CLOSED and NOW_CLOSED.
6     executeNowClosed();
7     break;
8 }

若是你真的想要fall-through,你可使用一個continue聲明和一個標籤:

 1 var command = 'CLOSED';
 2 switch (command) {
 3   case 'CLOSED':
 4     executeClosed();
 5     continue nowClosed;
 6   // Continues executing at the nowClosed label.
 7 
 8   nowClosed:
 9   case 'NOW_CLOSED':
10     // Runs for both CLOSED and NOW_CLOSED.
11     executeNowClosed();
12     break;
13 }

一個case條款能夠有局部變量,只有內部的條款的範圍是可見的。

斷言:Assert

assert若是布爾條件爲false,則使用語句來中斷正常執行。您能夠在本導覽中找到斷言語句的示例。這裏還有一些:

1 // Make sure the variable has a non-null value.
2 assert(text != null);
3 
4 // Make sure the value is less than 100.
5 assert(number < 100);
6 
7 // Make sure this is an https URL.
8 assert(urlString.startsWith('https'));

注意: 斷言語句對生產代碼沒有影響; 他們只是爲了發展。Flutter在調試模式下啓用斷言 僅限開發的工具(如dartdevc) 一般默認支持斷言。一些工具,如dartdart2js, 經過命令行標誌支持斷言:--enable-asserts

要將消息附加到斷言,請添加一個字符串做爲第二個參數。

1 assert(urlString.startsWith('https'),
2     'URL ($urlString) should start with "https".');

第一個參數assert能夠是任何解析爲布爾值的表達式。若是表達式的值爲true,則斷言成功並繼續執行。若是爲false,則斷言失敗並拋出異常( AssertionError)。

異常:Exceptions

您的Dart代碼能夠拋出並捕獲異常。例外是指示發生意外事件的錯誤。若是未捕獲異常,則會引起引起異常的隔離,而且一般會隔離隔離及其程序。

與Java相比,Dart的全部異常都是未經檢查的異常。方法不會聲明它們可能引起的異常,而且您不須要捕獲任何異常。

Dart提供了ExceptionError 類型,以及許多預約義的子類型。固然,您能夠定義本身的例外狀況。可是,Dart程序能夠拋出任何非null對象 - 不只僅是Exception和Error對象 - 做爲例外。

throw

如下是拋出或引起異常的示例:

throw FormatException('Expected at least 1 section');

你也能夠拋出任意對象:

throw 'Out of llamas!';

 

注意:生產質量代碼一般會拋出實現錯誤異常的類型 。

由於拋出異常是一個表達式,因此能夠在=>語句中以及容許表達式的任何其餘地方拋出異常:

void distanceTo(Point other) => throw UnimplementedError();

 

Catch

捕獲或捕獲異常會阻止異常傳播(除非您從新拋出異常)。捕獲異常使您有機會處理它:

1 try {
2   breedMoreLlamas();
3 } on OutOfLlamasException {
4   buyMoreLlamas();
5 }

要處理可能拋出多種類型異常的代碼,能夠指定多個catch子句。與拋出對象的類型匹配的第一個catch子句處理異常。若是catch子句未指定類型,則該子句能夠處理任何類型的拋出對象:

 1 try {
 2   breedMoreLlamas();
 3 } on OutOfLlamasException {
 4   // A specific exception
 5   buyMoreLlamas();
 6 } on Exception catch (e) {
 7   // Anything else that is an exception
 8   print('Unknown exception: $e');
 9 } catch (e) {
10   // No specified type, handles all
11   print('Something really unknown: $e');
12 }

正如上面的代碼所示,您可使用oncatch或二者兼而有之。使用on時須要指定異常類型。使用catch時,你的異常處理程序須要異常對象。

您能夠指定一個或兩個參數catch()。第一個是拋出的異常,第二個是堆棧跟蹤(StackTrace對象)。

1 try {
2   // ···
3 } on Exception catch (e) {
4   print('Exception details:\n $e');
5 } catch (e, s) {
6   print('Exception details:\n $e');
7   print('Stack trace:\n $s');
8 }

要部分處理異常,同時容許它傳播,請使用rethrow關鍵字。

 1 void misbehave() {
 2   try {
 3     dynamic foo = true;
 4     print(foo++); // Runtime error
 5   } catch (e) {
 6     print('misbehave() partially handled ${e.runtimeType}.');
 7     rethrow; // Allow callers to see the exception.
 8   }
 9 }
10 
11 void main() {
12   try {
13     misbehave();
14   } catch (e) {
15     print('main() finished handling ${e.runtimeType}.');
16   }
17 }

finally

不管是否拋出異常,要確保某些代碼運行,請使用finally子句。若是沒有catch子句匹配該異常,則在finally子句運行後傳播異常:

1 try {
2   breedMoreLlamas();
3 } finally {
4   // Always clean up, even if an exception is thrown.
5   cleanLlamaStalls();
6 }

finally子句在任何匹配的catch子句以後運行:

1 try {
2   breedMoreLlamas();
3 } catch (e) {
4   print('Error: $e'); // Handle the exception first.
5 } finally {
6   cleanLlamaStalls(); // Then clean up.
7 }

閱讀 Libraries之旅的例外部分,瞭解更多信息 。

Dart是一種面向對象的語言,具備類和基於mixin的繼承。每一個對象都是一個類的實例,全部類都來自Object。 基於Mixin的繼承意味着雖然每一個類(除了Object)只有一個超類,可是類體能夠在多個類層次結構中重用。

使用班級成員

對象具備由函數和數據(分別爲方法和 實例變量)組成的成員。調用方法時,能夠 在對象上調用它:該方法能夠訪問該對象的函數和數據。

使用點(.)來引用實例變量或方法:

 1 var p = Point(2, 2);
 2 
 3 // Set the value of the instance variable y.
 4 p.y = 3;
 5 
 6 // Get the value of y.
 7 assert(p.y == 3);
 8 
 9 // Invoke distanceTo() on p.
10 num distance = p.distanceTo(Point(4, 4));

當最左邊的操做數爲null時,使用?.而不是.避免異常:

1 // If p is non-null, set its y value to 4.
2 p?.y = 4;

使用構造函數

您可使用構造函數建立對象。構造函數名稱能夠是ClassName或 。例如,如下代碼使用和構造函數建立對象:ClassName.identifierPointPoint()Point.fromJson()

1 var p1 = Point(2, 2);
2 var p2 = Point.fromJson({'x': 1, 'y': 2});

如下代碼具備相同的效果,但new在構造函數名稱以前使用可選關鍵字:

1 var p1 = new Point(2, 2);
2 var p2 = new Point.fromJson({'x': 1, 'y': 2});

版本注:該new關鍵字Dart2成爲可選項。

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

var p = const ImmutablePoint(2, 2);

構造兩個相同的編譯時常量會產生一個規範的實例:

1 var a = const ImmutablePoint(1, 1);
2 var b = const ImmutablePoint(1, 1);
3 
4 assert(identical(a, b)); // They are the same instance!

在常量上下文中,您能夠省略const構造函數或文字以前的內容。例如,查看此代碼,該代碼建立一個const映射:

//這裏有不少const關鍵字。常量pointAndLine = const的{ '點' 常量[ 常量ImmutablePoint 0 0 )],'線' 常數[ 常量ImmutablePoint 1 10 ),常量ImmutablePoint ( - 2 11 )],};

您能夠省略除const關鍵字的第一次使用以外的全部內容:

1 // Lots of const keywords here.
2 const pointAndLine = const {
3   'point': const [const ImmutablePoint(0, 0)],
4   'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
5 };

若是常量構造函數在常量上下文以外而且在沒有const它的狀況下調用,則會建立一個很是量對象:

1 var a = const ImmutablePoint(1, 1); // Creates a constant
2 var b = ImmutablePoint(1, 1); // Does NOT create a constant
3 
4 assert(!identical(a, b)); // NOT the same instance!

版本注:該const關鍵字成爲Dart2的恆定範圍內可選。

獲取對象的類型

要在運行時獲取對象的類型,可使用Object的runtimeType屬性,該屬性返回Type對象。

print 'a的類型是$ {a.runtimeType}' );

到目前爲止,您已經瞭解瞭如何使用類。本節的其他部分將介紹如何實現類。

實例變量

如下是聲明實例變量的方法:

print('The type of a is ${a.runtimeType}');

全部未初始化的實例變量都具備該值null

全部實例變量都生成一個隱式getter方法。非最終實例變量也會生成隱式setter方法。有關詳細信息,請參閱Getters和setter

1 class Point {
2   num x; // Declare instance variable x, initially null.
3   num y; // Declare y, initially null.
4   num z = 0; // Declare z, initially 0.
5 }

若是初始化聲明它的實例變量(而不是構造函數或方法),則在建立實例時設置該值,該實例在構造函數及其初始化列表執行以前。

構造函數

經過建立與其類同名的函數來聲明構造函數(另外,可選地,如命名構造函數中所述的附加標識符 )。最多見的構造函數形式,即生成構造函數,建立一個類的新實例:

 1 class Point {
 2   num x;
 3   num y;
 4 }
 5 
 6 void main() {
 7   var point = Point();
 8   point.x = 4; // Use the setter method for x.
 9   assert(point.x == 4); // Use the getter method for x.
10   assert(point.y == null); // Values default to null.
11 }

this關鍵字是指當前實例。

注意: 使用this時,纔會有一個名稱衝突。不然,Dart風格省略了this

將構造函數參數賦值給實例變量的模式是如此常見,Dart具備語法糖,使其變得簡單:

1 class Point {
2   num x, y;
3 
4   Point(num x, num y) {
5     // There's a better way to do this, stay tuned.
6     this.x = x;
7     this.y = y;
8   }
9 }

默認構造函數

若是您未聲明構造函數,則會爲您提供默認構造函數。默認構造函數沒有參數,並在超類中調用無參數構造函數。

構造函數不是繼承的

子類不從其超類繼承構造函數。聲明沒有構造函數的子類只有默認(無參數,無名稱)構造函數。

命名構造函數

使用命名構造函數爲類實現多個構造函數或提供額外的清晰度:

 1 class Point {
 2   num x, y;
 3 
 4   Point(this.x, this.y);
 5 
 6   // Named constructor
 7   Point.origin() {
 8     x = 0;
 9     y = 0;
10   }
11 }

請記住,構造函數不是繼承的,這意味着超類的命名構造函數不會被子類繼承。若是但願使用超類中定義的命名構造函數建立子類,則必須在子類中實現該構造函數。

調用非默認的超類構造函數

默認狀況下,子類中的構造函數調用超類的未命名的無參數構造函數。超類的構造函數在構造函數體的開頭被調用。若是 還使用初始化列表,則在調用超類以前執行。總之,執行順序以下:

  1. 初始化列表
  2. 超類的無參數構造函數
  3. 主類的無參數構造函數

若是超類沒有未命名的無參數構造函數,則必須手動調用超類中的一個構造函數。在冒號(:)以後,在構造函數體(若是有)以前指定超類構造函數。

在下面的示例中,Employee類的構造函數爲其超類Person調用命名構造函數。單擊運行按鈕以執行代碼。

由於在調用構造函數以前會計算超類構造函數的參數,因此參數能夠是一個表達式,例如函數調用:

1 class Employee extends Person {
2   Employee() : super.fromJson(getDefaultData());
3   // ···
4 }

警告: 超類構造函數的參數無權訪問this。例如,參數能夠調用靜態方法,但不能調用實例方法。

初始化list

除了調用超類構造函數以外,還能夠在構造函數體運行以前初始化實例變量。用逗號分隔初始化程序。

1 // Initializer list sets instance variables before
2 // the constructor body runs.
3 Point.fromJson(Map<String, num> json)
4     : x = json['x'],
5       y = json['y'] {
6   print('In Point.fromJson(): ($x, $y)');
7 }

警告: 初始化程序的右側無權訪問this

在開發期間,您能夠經過assert在初始化列表中使用來驗證輸入。

1 Point.withAssert(this.x, this.y) : assert(x >= 0) {
2   print('In Point.withAssert(): ($x, $y)');
3 }

設置最終字段時,初始化程序列表很方便。如下示例初始化初始化列表中的三個最終字段。單擊運行按鈕以執行代碼。

重定向構造函數

有時構造函數的惟一目的是重定向到同一個類中的另外一個構造函數。重定向構造函數的主體是空的,構造函數調用出如今冒號(:)以後。

1 class Point {
2   num x, y;
3 
4   // The main constructor for this class.
5   Point(this.x, this.y);
6 
7   // Delegates to the main constructor.
8   Point.alongXAxis(num x) : this(x, 0);
9 }

常量構造函數

若是您的類生成永遠不會更改的對象,則可使這些對象成爲編譯時常量。爲此,請定義const構造函數並確保全部實例變量都是final

1 class ImmutablePoint {
2   static final ImmutablePoint origin =
3       const ImmutablePoint(0, 0);
4 
5   final num x, y;
6 
7   const ImmutablePoint(this.x, this.y);
8 }

常量構造函數並不老是建立常量。有關詳細信息,請參閱有關使用構造函數的部分 。

工廠構造函數:Factory constructors

factory在實現不老是建立其類的新實例的構造函數時使用關鍵字。例如,工廠構造函數可能從緩存中返回實例,或者它可能返回子類型的實例。

如下示例演示了從緩存中返回對象的工廠構造函數:

 1 class Logger {
 2   final String name;
 3   bool mute = false;
 4 
 5   // _cache is library-private, thanks to
 6   // the _ in front of its name.
 7   static final Map<String, Logger> _cache =
 8       <String, Logger>{};
 9 
10   factory Logger(String name) {
11     if (_cache.containsKey(name)) {
12       return _cache[name];
13     } else {
14       final logger = Logger._internal(name);
15       _cache[name] = logger;
16       return logger;
17     }
18   }
19 
20   Logger._internal(this.name);
21 
22   void log(String msg) {
23     if (!mute) print(msg);
24   }
25 }

注意: 工廠構造函數無權訪問this

像調用任何其餘構造函數同樣調用工廠構造函數:

1 var logger = Logger('UI');
2 logger.log('Button clicked');

方法

方法是爲對象提供行爲的函數。

實例方法

對象的實例方法能夠訪問實例變量和this。在distanceTo()下面的示例中方法是一個實例方法的一個例子:

 1 import 'dart:math';
 2 
 3 class Point {
 4   num x, y;
 5 
 6   Point(this.x, this.y);
 7 
 8   num distanceTo(Point other) {
 9     var dx = x - other.x;
10     var dy = y - other.y;
11     return sqrt(dx * dx + dy * dy);
12   }
13 }

Getters 和 setters

getter和setter是提供對象屬性的讀寫訪問權限的特殊方法。回想一下,每一個實例變量都有一個隱式getter,若是合適的話還有一個setter。您可使用getset關鍵字經過實現getter和setter來建立其餘屬性 :

 1 class Rectangle {
 2   num left, top, width, height;
 3 
 4   Rectangle(this.left, this.top, this.width, this.height);
 5 
 6   // Define two calculated properties: right and bottom.
 7   num get right => left + width;
 8   set right(num value) => left = value - width;
 9   num get bottom => top + height;
10   set bottom(num value) => top = value - height;
11 }
12 
13 void main() {
14   var rect = Rectangle(3, 4, 20, 15);
15   assert(rect.left == 3);
16   rect.right = 12;
17   assert(rect.left == -8);
18 }

使用getter和setter,您能夠從實例變量開始,稍後使用方法包裝它們,而無需更改客戶端代碼。

注意: 不管是否明肯定義了getter,增量(++)等運算符都以預期的方式工做。爲避免任何意外的反作用,操做員只需調用一次getter,將其值保存在臨時變量中。

抽象方法

實例,getter和setter方法能夠是抽象的,定義一個接口,但將其實現留給其餘類。抽象方法只能存在於抽象類中

要使方法成爲抽象,請使用分號(;)而不是方法體:

 1 abstract class Doer {
 2   // Define instance variables and methods...
 3 
 4   void doSomething(); // Define an abstract method.
 5 }
 6 
 7 class EffectiveDoer extends Doer {
 8   void doSomething() {
 9     // Provide an implementation, so the method is not abstract here...
10   }
11 }

抽象類

使用abstract修飾符定義抽象類 - 沒法實例化的類。抽象類對於定義接口很是有用,一般還有一些實現。若是但願抽象類看起來是可實例化的,請定義工廠構造函數

抽象類一般有抽象方法。這是一個聲明具備抽象方法的抽象類的示例:

1 // This class is declared abstract and thus
2 // can't be instantiated.
3 abstract class AbstractContainer {
4   // Define constructors, fields, methods...
5 
6   void updateChildren(); // Abstract method.
7 }

隱式接口

每一個類都隱式定義一個接口,該接口包含該類的全部實例成員及其實現的任何接口。若是要在不繼承B實現的狀況下建立支持B類API的A類,則A類應實現B接口。

類經過在implements子句中聲明它們而後提供接口所需的API來實現一個或多個 接口。例如:

 1 // A person. The implicit interface contains greet().
 2 class Person {
 3   // In the interface, but visible only in this library.
 4   final _name;
 5 
 6   // Not in the interface, since this is a constructor.
 7   Person(this._name);
 8 
 9   // In the interface.
10   String greet(String who) => 'Hello, $who. I am $_name.';
11 }
12 
13 // An implementation of the Person interface.
14 class Impostor implements Person {
15   get _name => '';
16 
17   String greet(String who) => 'Hi $who. Do you know who I am?';
18 }
19 
20 String greetBob(Person person) => person.greet('Bob');
21 
22 void main() {
23   print(greetBob(Person('Kathy')));
24   print(greetBob(Impostor()));
25 }

這是一個指定類實現多個接口的示例:

class Point implements Comparable, Location {...}

擴展類

使用extends建立一個子類,並super指超:

 1 class Television {
 2   void turnOn() {
 3     _illuminateDisplay();
 4     _activateIrSensor();
 5   }
 6   // ···
 7 }
 8 
 9 class SmartTelevision extends Television {
10   void turnOn() {
11     super.turnOn();
12     _bootNetworkInterface();
13     _initializeMemory();
14     _upgradeApps();
15   }
16   // ···
17 }

覆蓋成員:Overriding members

子類能夠覆蓋實例方法,getter和setter。您可使用@override註釋來指示您有意覆蓋成員:

1 class SmartTelevision extends Television {
2   @override
3   void turnOn() {...}
4   // ···
5 }

要在類型安全的代碼中縮小方法參數或實例變量的 類型,可使用covariant關鍵字

可覆蓋的運算符

您能夠覆蓋下表中顯示的運算符。例如,若是定義Vector類,則能夠定義+添加兩個向量的方法。

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>  

注意:您可能已經注意到,這!=不是可覆蓋的運算符。表達e1 != e2只是語法糖!(e1 == e2)

這是一個覆蓋+-運算符的類的示例:

 1 class Vector {
 2   final int x, y;
 3 
 4   Vector(this.x, this.y);
 5 
 6   Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
 7   Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
 8 
 9   // Operator == and hashCode not shown. For details, see note below.
10   // ···
11 }
12 
13 void main() {
14   final v = Vector(2, 3);
15   final w = Vector(2, 2);
16 
17   assert(v + w == Vector(4, 5));
18   assert(v - w == Vector(0, 1));
19 }

若是覆蓋==,則還應覆蓋Object的hashCodegetter。用於覆蓋的一個例子==hashCode,請參見 實施映射鍵

有關覆蓋的更多信息,請參閱 擴展類

noSuchMethod()

要在代碼嘗試使用不存在的方法或實例變量時檢測或作出反應,您能夠覆蓋noSuchMethod()

1 class A {
2   // Unless you override noSuchMethod, using a
3   // non-existent member results in a NoSuchMethodError.
4   @override
5   void noSuchMethod(Invocation invocation) {
6     print('You tried to use a non-existent member: ' +
7         '${invocation.memberName}');
8   }
9 }

你不能調用,除非未實現的方法, 一個如下是真實的:

  • 接收器具備靜態類型dynamic

  • 接收器有一個靜態類型,它定義了未實現的方法(抽象是OK),接收器的動態類型的實現與類noSuchMethod() 中的實現不一樣Object

有關更多信息,請參閱非正式 noSuchMethod轉發規範。

 

枚舉類型

枚舉類型(一般稱爲枚舉或枚舉)是一種特殊類,用於表示固定數量的常量值。

使用枚舉

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

enum Color { red, green, blue }

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

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

要獲取枚舉中全部值的列表,請使用枚舉values常量。

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

您能夠在switch語句中使用枚舉,若是您不處理全部枚舉值,您將收到警告:

 1 var aColor = Color.blue;
 2 
 3 switch (aColor) {
 4   case Color.red:
 5     print('Red as roses!');
 6     break;
 7   case Color.green:
 8     print('Green as grass!');
 9     break;
10   default: // Without this, you see a WARNING.
11     print(aColor); // 'Color.blue'
12 }

枚舉類型具備如下限制:

  • 您不能子類化,混合或實現枚舉。
  • 您沒法顯式實例化枚舉。

有關更多信息,請參閱Dart語言規範

向類添加功能:mixins

Mixins是一種在多個類層次結構中重用類代碼的方法。

要使用 mixin,請使用with關鍵字後跟一個或多個mixin名稱。如下示例顯示了兩個使用mixins的類:

 1 class Musician extends Performer with Musical {
 2   // ···
 3 }
 4 
 5 class Maestro extends Person
 6     with Musical, Aggressive, Demented {
 7   Maestro(String maestroName) {
 8     name = maestroName;
 9     canConduct = true;
10   }
11 }

要實現 mixin,請建立一個擴展Object的類,而且不聲明構造函數。除非您但願mixin可用做常規類,不然請使用mixin關鍵字而不是class。例如:

 1 mixin Musical {
 2   bool canPlayPiano = false;
 3   bool canCompose = false;
 4   bool canConduct = false;
 5 
 6   void entertainMe() {
 7     if (canPlayPiano) {
 8       print('Playing piano');
 9     } else if (canConduct) {
10       print('Waving hands');
11     } else {
12       print('Humming to self');
13     }
14   }
15 }

要指定只有某些類型可使用mixin - 例如,因此你的mixin能夠調用它沒有定義的方法 - 用於on指定所需的超類:

1 mixin MusicalPerformer on Musician {
2   // ···
3 }

版本說明:mixin Dart 2.1中引入了對關鍵字的支持。一般使用早期版本中的代碼abstract class。有關2.1 mixin更改的更多信息,請參閱 Dart SDK changelog2.1 mixin規範。

類變量和方法

使用static關鍵字實現類範圍的變量和方法。

靜態變量

靜態變量(類變量)對於類範圍的狀態和常量頗有用:

1 class Queue {
2   static const initialCapacity = 16;
3   // ···
4 }
5 
6 void main() {
7   assert(Queue.initialCapacity == 16);
8 }

靜態變量在使用以前不會初始化。

注意: 此頁面遵循 首選的常量名稱的樣式指南建議lowerCamelCase

靜態方法

靜態方法(類方法)不對實例進行操做,所以無權訪問this。例如:

 1 import 'dart:math';
 2 
 3 class Point {
 4   num x, y;
 5   Point(this.x, this.y);
 6 
 7   static num distanceBetween(Point a, Point b) {
 8     var dx = a.x - b.x;
 9     var dy = a.y - b.y;
10     return sqrt(dx * dx + dy * dy);
11   }
12 }
13 
14 void main() {
15   var a = Point(2, 2);
16   var b = Point(4, 4);
17   var distance = Point.distanceBetween(a, b);
18   assert(2.8 < distance && distance < 2.9);
19   print(distance);
20 }

注意: 對於經常使用或普遍使用的實用程序和功能,請考慮使用頂級函數而不是靜態方法。

您可使用靜態方法做爲編譯時常量。例如,您能夠將靜態方法做爲參數傳遞給常量構造函數。

泛型

若是您查看基本數組類型的API文檔 List,您會看到該類型其實是List<E>。<...>表示法將List標記爲 通用(或參數化)類型 - 具備正式類型參數的類型。按照慣例,大多數類型變量都有單字母名稱,例如E,T,S,K和V.

爲何要使用泛型?

類型安全一般須要泛型,但它們比僅容許代碼運行有更多好處:

  • 正確指定泛型類型會產生更好的生成代碼。
  • 您可使用泛型來減小代碼重複。

若是您但願列表只包含字符串,則能夠將其聲明爲List<String>(將其讀做「字符串列表」)。這樣,您,您的程序員和您的工具能夠檢測到將非字符串分配給列表多是一個錯誤。這是一個例子:

1 var names = List<String>();
2 names.addAll(['Seth', 'Kathy', 'Lars']);
3 names.add(42); // Error

使用泛型的另外一個緣由是減小代碼重複。泛型容許您在多種類型之間共享單個接口和實現,同時仍然利用靜態分析。例如,假設您建立了一個用於緩存對象的接口:

1 abstract class ObjectCache {
2   Object getByKey(String key);
3   void setByKey(String key, Object value);
4 }

您發現須要此接口的特定於字符串的版本,所以您須要建立另外一個接口:

1 abstract class StringCache {
2   String getByKey(String key);
3   void setByKey(String key, String value);
4 }

以後,您決定要使用此接口的數字版本...您明白了。

通用類型能夠省去建立全部這些接口的麻煩。相反,您能夠建立一個帶有類型參數的接口:

1 abstract class Cache<T> {
2   T getByKey(String key);
3   void setByKey(String key, T value);
4 }

在此代碼中,T是替身類型。它是一個佔位符,您能夠將其視爲開發人員稍後定義的類型。

使用集合文字

能夠參數化列表,集和Hash集合文字。參數化文字就像你已經看到的文字同樣,除了你在開始括號以前添加 (對於列表和集合)或 (對於Hash集合)。如下是使用類型文字的示例:<type><keyType, valueType>

1 var names = <String>['Seth', 'Kathy', 'Lars'];
2 var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
3 var pages = <String, String>{
4   'index.html': 'Homepage',
5   'robots.txt': 'Hints for web robots',
6   'humans.txt': 'We are people, not machines'
7 };

使用帶有構造函數的參數化類型

要在使用構造函數時指定一個或多個類型,請將類型放在<...>類名稱後面的尖括號()中。例如:

var nameSet = Set<String>.from(names);

如下代碼建立一個具備整數鍵和View類型值的映射:

var views = Map<int, View>();

通用集合及其包含的類型

Dart泛型類型的具體化,這意味着他們隨身攜帶的類型信息在運行時。例如,您能夠測試集合的類型:

1 var names = List<String>();
2 names.addAll(['Seth', 'Kathy', 'Lars']);
3 print(names is List<String>); // true

注意: 相反,Java中的泛型使用擦除,這意味着在運行時刪除泛型類型參數。在Java中,您能夠測試對象是否爲List,但您沒法測試它是否爲a List<String>

限制參數化類型

實現泛型類型時,您可能但願限制其參數的類型。你可使用extends

1 class Foo<T extends SomeBaseClass> {
2   // Implementation goes here...
3   String toString() => "Instance of 'Foo<$T>'";
4 }
5 
6 class Extender extends SomeBaseClass {...}

使用SomeBaseClass或其任何子類做爲通用參數是能夠的:

1 var someBaseClassFoo = Foo<SomeBaseClass>();
2 var extenderFoo = Foo<Extender>();

也能夠不指定泛型參數:

1 var foo = Foo();
2 print(foo); // Instance of 'Foo<SomeBaseClass>'

指定任何非SomeBaseClass類型會致使錯誤:

var foo = Foo<Object>();

使用通用方法

最初,Dart的通用支持僅限於課程。一種稱爲泛型方法的新語法容許在方法和函數上使用類型參數:

1 T first<T>(List<T> ts) {
2   // Do some initial work or error checking, then...
3   T tmp = ts[0];
4   // Do some additional checking or processing...
5   return tmp;
6 }

這裏on first<T>)的泛型類型參數容許你T在幾個地方使用type參數:

  • 在函數的返回類型(T)中。
  • 在參數類型(List<T>)中。
  • 在局部變量的類型(T tmp)。

有關泛型的更多信息,請參閱 使用泛型方法。

Libraries和visibility

importlibrary指令能夠幫助您建立一個模塊化的,可共享的代碼庫。庫不只提供API,仍是隱私單元:如下劃線(_)開頭的標識符僅在庫內可見。每一個Dart應用程序都是一個庫,即便它不使用library指令。

可使用包來分發庫。 有關pub(包含在SDK中的包管理器)的信息,請參閱 Pub Package和Asset Manager

使用庫

使用import指定如何從一個庫中的命名空間在另外一個庫的範圍內使用。

例如,Dart Web應用程序一般使用dart:html 庫,它們能夠像這樣導入:

import 'dart:html';

惟一須要的參數import是指定庫的URI。對於內置庫,URI具備特殊dart:方案。對於其餘庫,您可使用文件系統路徑或package: 方案。該package:方案指定由包管理器(如pub工具)提供的庫。例如:

import 'package:test/test.dart';

注意: URI表明統一資源標識符。 URL(統一資源定位符)是一種常見的URI。

指定庫前綴

若是導入兩個具備衝突標識符的庫,則能夠爲一個或兩個庫指定前綴。例如,若是library1和library2都有一個Element類,那麼你可能有這樣的代碼:

1 import 'package:lib1/lib1.dart';
2 import 'package:lib2/lib2.dart' as lib2;
3 
4 // Uses Element from lib1.
5 Element element1 = Element();
6 
7 // Uses Element from lib2.
8 lib2.Element element2 = lib2.Element();

僅導入庫的一部分

若是隻想使用庫的一部分,則能夠有選擇地導入庫。例如:

1 // Import only foo.
2 import 'package:lib1/lib1.dart' show foo;
3 
4 // Import all names EXCEPT foo.
5 import 'package:lib2/lib2.dart' hide foo;

懶惰地加載一個庫

延遲加載(也稱爲延遲加載)容許應用程序根據須要加載庫,若是須要的話。如下是您可能使用延遲加載的一些狀況:

  • 減小應用程序的初始啓動時間。
  • 例如,執行A / B測試 - 嘗試算法的替代實現。
  • 加載不多使用的功能,例如可選的屏幕和對話框。

要懶洋洋地加載庫,必須先使用它導入它deferred as

import 'package:greetings/hello.dart' deferred as hello;

當您須要庫時,loadLibrary()使用庫的標識符進行調用 。

1 Future greet() async {
2   await hello.loadLibrary();
3   hello.printGreeting();
4 }

在前面的代碼中,await關鍵字暫停執行,直到加載庫。有關詳細信息async,並await請參閱異步支持

您能夠loadLibrary()在庫上屢次調用而不會出現問題。該庫只加載一次。

使用延遲加載時請記住如下內容:

  • 延遲庫的常量不是導入文件中的常量。請記住,在加載延遲庫以前,這些常量不存在。
  • 您不能在導入文件中使用延遲庫中的類型。相反,請考慮將接口類型移動到由延遲庫和導入文件導入的庫。
  • Dart隱式插入loadLibrary()您使用的命名空間。該函數返回Futuredeferred asnamespaceloadLibrary()

Dart VM差別: 即便在調用以前,Dart VM也容許訪問延遲庫的成員loadLibrary()。此行爲可能會更改,所以 不要依賴於當前的VM行爲。 有關詳細信息,請參閱問題#33118。

實現庫

有關 如何實現庫包的建議,請參閱 建立庫包,包括:

  • 如何組織庫源代碼。
  • 如何使用該export指令。
  • 什麼時候使用該part指令。
  • 什麼時候使用該library指令。

 

異步支持

Dart庫中包含許多返回FutureStream對象的函數。這些函數是異步的:它們在設置可能耗時的操做(例如I / O)後返回,而不等待該操做完成。

asyncawait關鍵字支持異步編程,讓你寫異步代碼看起來相似於同步代碼。

 

處理期貨

當您須要完成Future的結果時,您有兩個選擇:

使用asyncawait異步的代碼,但它看起來很像同步代碼。例如,這裏有一些代碼await 用於等待異步函數的結果:

await lookUpVersion();

要使用await,代碼必須在異步函數中 - 標記爲的函數async

1 Future checkVersion() async {
2   var version = await lookUpVersion();
3   // Do something with version
4 }

注意: 雖然異步函數可能會執行耗時的操做,但它不會等待這些操做。相反,異步函數只在遇到第一個await表達式(詳細信息)時執行。而後它返回一個Future對象,僅在await表達式完成後才恢復執行。

使用trycatchfinally 處理使用await如下代碼的錯誤和清理:

1 try {
2   version = await lookUpVersion();
3 } catch (e) {
4   // React to inability to look up the version
5 }

您能夠await在異步功能中屢次使用。例如,如下代碼等待三次函數結果:

1 var entrypoint = await findEntrypoint();
2 var exitCode = await runExecutable(entrypoint, args);
3 await flushThenExit(exitCode);

在,價值一般是將來; 若是不是,那麼該值將自動包裝在Future中。此Future對象表示返回對象的承諾。值是返回的對象。await表達式使執行暫停,直到該對象可用。await expressionexpressionawait expression

若是在使用時出現編譯時錯誤await,請確保await處於異步功能中。 例如,要await在您的應用程序的main()功能中使用,main()必須將正文標記爲async

1 Future main() async {
2   checkVersion();
3   print('In main: version is ${await lookUpVersion()}');
4 }

聲明異步函數

一個異步函數是一個函數體標有async修改。

async關鍵字添加到函數使其返回Future。例如,考慮這個同步函數,它返回一個String:

String lookUpVersion() => '1.0.0';

若是將其更改成異步函數 - 例如,由於未來的實現將很是耗時 - 返回的值是Future:

Future<String> lookUpVersion() async => '1.0.0';

請注意,函數的主體不須要使用Future API。若有必要,Dart會建立Future對象。

若是您的函數沒有返回有用的值,請設置其返回類型Future<void>

處理流

當您須要從Stream獲取值時,您有兩個選擇:

  • 使用async和異步for循環(await for)。
  • 使用Stream API,如 庫瀏覽中所述

注意: 在使用以前await for,請確保它使代碼更清晰,而且您確實但願等待全部流的結果。例如,你一般應該不使用await for的UI事件偵聽器,由於UI框架發送事件的人流如潮。

異步for循環具備如下形式:

await for (varOrType identifier in expression) {
  // Executes each time the stream emits a value.
}

expression必須具備Stream類型。執行過程以下:

  1. 等到流發出一個值。
  2. 執行for循環的主體,將變量設置爲該發出的值。
  3. 重複1和2,直到關閉流。

要中止偵聽流,可使用breakor return語句,該for語句會從for循環中斷開並從流中取消取消。

若是在實現異步for循環時遇到編譯時錯誤,請確保await for它處於異步函數中。 例如,要在應用程序的main()函數中使用異步for循環,main()必須將正文標記爲async

1 Future main() async {
2   // ...
3   await for (var request in requestServer) {
4     handleRequest(request);
5   }
6   // ...
7 }

有關異步編程的更多信息,請參閱 庫瀏覽的 dart:async部分。另請參閱文章 Dart語言異步支持:階段1 和Dart語言異步支持:階段2Dart語言規範

Generators

當您須要懶惰地生成一系列值時,請考慮使用生成器函數。Dart內置支持兩種Generators功能:

  • 同步生成器:返回一個Iterable對象。
  • 異步生成器:返回Stream對象。

要實現同步生成器函數,請將函數體標記爲sync*,並使用yield語句來傳遞值:

1 Iterable<int> naturalsTo(int n) sync* {
2   int k = 0;
3   while (k < n) yield k++;
4 }

要實現異步生成器函數,請將函數體標記爲async*,並使用yield語句來傳遞值:

1 Stream<int> asynchronousNaturalsTo(int n) async* {
2   int k = 0;
3   while (k < n) yield k++;
4 }

若是您的生成器是遞歸的,您可使用yield*如下方法來提升其性能:

1 Iterable<int> naturalsDownFrom(int n) sync* {
2   if (n > 0) {
3     yield n;
4     yield* naturalsDownFrom(n - 1);
5   }
6 }

有關生成器的更多信息,請參閱文章 Dart語言異步支持:階段2

可調用的類

要容許像函數同樣調用Dart類,請實現該call()方法。

在下面的示例中,WannabeFunction該類定義了一個call()函數,它接受三個字符串並鏈接它們,用空格分隔每一個字符串,並附加一個感嘆號。單擊運行按鈕以執行代碼。

有關處理函數類的更多信息,請參閱 在Dart中模擬函數

分離

大多數計算機,即便在移動平臺上,也有多核CPU。爲了利用全部這些核心,開發人員傳統上使用併發運行的共享內存線程。可是,共享狀態併發容易出錯,而且可能致使代碼複雜化。

全部Dart代碼都在隔離區內運行,而不是線程。每一個隔離區都有本身的內存堆,確保不會從任何其餘隔離區訪問隔離區的狀態。

有關更多信息,請參閱 dart:isolate庫文檔。

類型定義

在Dart中,函數是對象,就像字符串同樣,數字是對象。一個類型定義,或功能型的別名,給出了一個函數類型聲明字段時,您可使用和返回類型的名稱。當函數類型分配給變量時,typedef會保留類型信息。

請考慮如下代碼,它不使用typedef:

 1 class SortedCollection {
 2   Function compare;
 3 
 4   SortedCollection(int f(Object a, Object b)) {
 5     compare = f;
 6   }
 7 }
 8 
 9 // Initial, broken implementation.
10 int sort(Object a, Object b) => 0;
11 
12 void main() {
13   SortedCollection coll = SortedCollection(sort);
14 
15   // All we know is that compare is a function,
16   // but what type of function?
17   assert(coll.compare is Function);
18 }

當分配類型信息丟失fcompare。類型 f(Object, Object)→ int(其中→表示返回),但類型compare是功能。若是咱們將代碼更改成使用顯式名稱並保留類型信息,則開發人員和工具均可以使用該信息。

 1 typedef Compare = int Function(Object a, Object b);
 2 
 3 class SortedCollection {
 4   Compare compare;
 5 
 6   SortedCollection(this.compare);
 7 }
 8 
 9 // Initial, broken implementation.
10 int sort(Object a, Object b) => 0;
11 
12 void main() {
13   SortedCollection coll = SortedCollection(sort);
14   assert(coll.compare is Function);
15   assert(coll.compare is Compare);
16 }

注意: 目前,typedef僅限於函數類型。咱們但願這會改變。

由於typedef只是別名,因此它們提供了一種檢查任何函數類型的方法。例如:

1 typedef Compare<T> = int Function(T a, T b);
2 
3 int sort(int a, int b) => a - b;
4 
5 void main() {
6   assert(sort is Compare<int>); // True!
7 }

元數據

使用元數據提供有關代碼的其餘信息。元數據註釋以字符開頭@,後跟對編譯時常量的引用(如deprecated)或對常量構造函數的調用。

全部Dart代碼都有兩個註釋:@deprecated和 @override。有關使用的示例@override,請參閱擴展類。如下是使用@deprecated 註釋的示例:

 1 class Television {
 2   /// _Deprecated: Use [turnOn] instead._
 3   @deprecated
 4   void activate() {
 5     turnOn();
 6   }
 7 
 8   /// Turns the TV's power on.
 9   void turnOn() {...}
10 }

您能夠定義本身的元數據註釋。這是一個定義帶有兩個參數的@todo註釋的示例:

1 library todo;
2 
3 class Todo {
4   final String who;
5   final String what;
6 
7   const Todo(this.who, this.what);
8 }

如下是使用@todo註釋的示例:

1 import 'todo.dart';
2 
3 @Todo('seth', 'make this do something')
4 void doSomething() {
5   print('do something');
6 }

元數據能夠出如今庫,類,typedef,類型參數,構造函數,工廠,函數,字段,參數或變量聲明以前以及導入或導出指令以前。您可使用反射在運行時檢索元數據。

註釋

Dart支持單行註釋,多行註釋和文檔註釋。

單行註釋

單行註釋以//。開頭。//Dart編譯器會忽略行之間和行尾的全部內容。

1 void main() {
2   // TODO: refactor into an AbstractLlamaGreetingFactory?
3   print('Welcome to my Llama farm!');
4 }

多行註釋

多行註釋以... /*結尾*/。介於二者之間的/*,並*/用Dart編譯器忽略(除非該註釋是一個文檔註釋;見下一節)。多行註釋能夠嵌套。

 1 void main() {
 2   /*
 3    * This is a lot of work. Consider raising chickens.
 4 
 5   Llama larry = Llama();
 6   larry.feed();
 7   larry.exercise();
 8   larry.clean();
 9    */
10 }

文檔註釋

文檔註釋是首先多行或單行註釋////**。使用///連續的行上有一個多行文檔註釋一樣的效果。

在文檔註釋中,Dart編譯器忽略全部文本,除非它括在括號中。使用括號,您能夠引用類,方法,字段,頂級變量,函數和參數。括號中的名稱在已記錄的程序元素的詞法範圍內獲得解析。

如下是文檔註釋的示例,其中引用了其餘類和參數:

 1 /// A domesticated South American camelid (Lama glama).
 2 ///
 3 /// Andean cultures have used llamas as meat and pack
 4 /// animals since pre-Hispanic times.
 5 class Llama {
 6   String name;
 7 
 8   /// Feeds your llama [Food].
 9   ///
10   /// The typical llama eats one bale of hay per week.
11   void feed(Food food) {
12     // ...
13   }
14 
15   /// Exercises your llama with an [activity] for
16   /// [timeLimit] minutes.
17   void exercise(Activity activity, int timeLimit) {
18     // ...
19   }
20 }

在生成的文檔中,[Food]成爲Food類的API文檔的連接。

要解析Dart代碼並生成HTML文檔,您可使用SDK的 文檔生成工具。 有關生成的文檔的示例,請參閱Dart API文檔。有關如何構建註釋的建議,請參閱 Dart Doc註釋指南。

相關文章
相關標籤/搜索