導覽
Dart編程語言導覽
本文展示如何使用各個主要Dart功能朴皆,從變量所踊、運算符到類和庫拥坛,這里假定你已經至少了解如何使用一種其它編程語言缀蹄。
要了解更多有關 Dart核心庫的知識,請參見核心庫導覽排拷。在想要了解更多有關語言功能的時候侧漓,請查詢Dart語言規(guī)范。
小貼士: 你可以借助DartPad來使用Dart語言的大部分功能 (了解更多)攻泼。
一個Dart基礎程序
以下代碼使用了Dart的大量最基礎的功能:
// 定義一個函數
printInteger(int aNumber) {
print('The number is $aNumber.'); // 打印到控制臺
}
// 這是應用開始執(zhí)行的地方火架。
main() {
var number = 42; // 聲明和初始化一個變量
printInteger(number); // 調用函數
}
以下是這個程序所使用到的可用于所有(或幾乎所有)的Dart應用的部分:
// 這是一條注釋
單行注釋。Dart還支持多行注釋和文件注釋忙菠。更多詳情何鸡,請參見 注釋.
int
一種類型。其它的一些 內置類型 有 String
, List
和 bool
牛欢。
42
>
一個數字字面量骡男。數字字面量是一種編譯時常量。
print()
一種顯示輸出的方便的方式傍睹。
'...'
(or "..."
)
一個字符串字面量隔盛。
$*variableName*
(或 ${*expression*}
)
插值字符串:包含等價于字符串字面量的變量或表達式的字符串犹菱。更多相關信息,請參見 字符串.
main()
在應用執(zhí)行開始處的特殊的吮炕、必須的頂級函數腊脱。更多相關信息,請參見 main()函數.
var
一種無需指定類型聲明變量的方式龙亲。
Note: 本站的代碼遵循 Dart 樣式指南中的準則陕凹。
重要概念
在學習Dart編程語言的過程中,請在心中保持以下事實和概念:
- 在變量中放置的所有內容都是對象鳄炉,并且每個對象都是一個類的實例杜耙。包括數字、函數和
null
都是對象拂盯。所有的對象都繼承自 Object 類佑女。 - 雖然Dart是強類型語言,類型標注卻是可選的谈竿,因為Dart可以推導類型团驱。在以上的代碼中,
number
所推導的類型是int
榕订。在你想要顯式地說明無需設定類型店茶, 使用特殊類型dynamic
. - Dart支持泛型,如
List<int>
(整型列表) 或List<dynamic>
(任意類型對象的列表)劫恒。 - Dart支持頂級函數(如
main()
)贩幻,以及與類或對象綁定的函數 (分別為靜態(tài)和實例方法)。你也可以在函數內創(chuàng)建函數(嵌套或局部函數)两嘴。 - 類型地丛楚,Dart支持頂級變量,以及與類或對象綁定的變量(靜態(tài)和實例變量)憔辫。實例變量有時也稱為字段或屬性趣些。
- 不同于Java,Dart沒有
public
,protected
和private
這些關鍵字贰您。 如果一個標識符以下劃線(_)開頭坏平,它是對庫私有的。詳情請參見 庫和可見性. - 標識符可以字母或下劃線 (_)開頭锦亦,后接這些字符和數字的任意組合舶替。
- Dart既有表達式(擁有運行時值)也有語句(沒有運行時值)。例如杠园, 條件表達式
condition ? expr1 : expr2
有一個值expr1
或expr2
顾瞪。對比 if-else 語句則沒有值。一條語句通常包含一個或多個表達式,但一個表達式不能直接包含一條語句陈醒。 - Dart工具可以報出兩類問題:警告(warning)和錯誤(error) 惕橙。警告只是表明你的代碼可能無法運行,但并不會阻止程序的執(zhí)行钉跷。錯誤可以是編譯時或運行時的弥鹦。編譯時錯誤會完全阻止代碼的執(zhí)行,運行時錯誤會在代碼執(zhí)行時導致 異常 的拋出爷辙。
關鍵字
下表中列出了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版本之后添加的與異步支持相關的更新的抓韩、有限的保留詞肴捉。你無法使用
async
,async*
或sync*
標記的函數體內使用await
或yield
作為標識符。
表中的其它詞都是保留詞乏德,無法用作標識符撤奸。
變量
以下是一個創(chuàng)建變量并初始化的示例:
var name = 'Bob';
變量存儲引用。名為name
的變量包含一個對值為”Bob”的String
對象的引用喊括。
變量name
的類型推導為String
胧瓜,但你可以通過指定類型來進行修改。如果對象不僅限于單個類型郑什,按照設計指南將其指定為Object
或dynamic
類型府喳。
dynamic name = 'Bob';
另一個選項是顯式地將其聲明為所要推導的類型:
String name = 'Bob';
Note: 本頁中對局部變量按照 樣式指南推薦 使用 var
,而非類型注解蘑拯。
默認值
未初始過的值有一個初始值null
钝满。即使是帶有數值類型的變量初始值也是null,因為數值類型和Dart中的所有內容一樣申窘,都是對象弯蚜。
int lineCount;
assert(lineCount == null);
Note: 生產代碼會忽略 assert()
調用。而在開發(fā)期間偶洋,當 if 條件為假時 assert(*condition*)
會拋出一個異常熟吏。詳情請參見 Assert。
Final和const
如果你不想要修改一個變量,使用 final
或 const
來替代 var
或加前類型前牵寺。final變量只能設置一次悍引,const變量是編譯時常量。(const變量是隱式的final)帽氓。final頂級或類變量在初次使用時進行初始化趣斤。
Note: 實例變量可以是 final
,但不能為 const
黎休。final實例變量必須在構造函數體開始之前進行初始化浓领,通過構造函數參數在變量聲明中或在構造函數的初始化程序列表中。
以下是一個創(chuàng)建和設置final變量的示例:
final name = 'Bob'; // 無類型標注
final String nickname = 'Bobby';
你無法修改final變量的值:
name = 'Alice'; // Error: a final variable can only be set once.
對你希望是編譯時常量的變量使用const
势腮。如果const變量是類級別的联贩,將其標記為 static const
。在聲明變量之處捎拯,設置值為編譯時常量泪幌,如數值或字符串字面量、const變量或對常數的算術運算結果:
const bar = 1000000; // 單位壓強 (dynes/cm2)
const double atm = 1.01325 * bar; // 標準大氣
const
關鍵字不只是為聲明常變量的署照。你還可以使用它來創(chuàng)建常量值祸泪,以及聲明創(chuàng)建常量值的構造函數。任意變量可擁有常量值建芙。
var foo = const [];
final bar = const [];
const baz = []; // 等價于 const []
你可以在const
聲明的初始化表達式中省略 const
没隘,像上面的 baz
。詳情請參見 不要重復使用const.
你可以修改一個非final禁荸、非const變量的值右蒲,即使它曾經是一個const值:
foo = [1, 2, 3]; // 曾經是const []
但是不能修改const變量的值:
baz = [42]; // Error: Constant variables can't be assigned a value.
更多有關使用 const
創(chuàng)建常量值的內容,請參見 列表, 映射 和 類屡限。
內置類型
Dart語言擁有對如下類型的特別支持:
- 數值
- 字符串
- 布爾型
- 列表 (也稱為數組)
- 集
- 映射
- rune (用于在字符串表示Unicode字符串)
- 符號
你可以使用字面量初始化任意這些特殊類型的對象品嚣。例如, 'this is a string'
是一個字符串字面量钧大, true
是一個布爾型字面量翰撑。
因為Dart中的每個變量都引用一個對象 – 一個類的實例,通嘲⊙耄可以使用構造函數來初始化變量眶诈。一些內置類型有它們自己的構造函數。例如瓜饥,你可以使用 Map()
構造函數來創(chuàng)建一個映射逝撬。
數值
Dart有兩種數值類型:
整型值根據平臺不大于64位。在 Dart VM中乓土,值可以為 -263 到 263 – 1宪潮。編譯為JavaScript的Dart使用 JavaScript數值溯警, 允許的值為 -253 到 253 – 1。
64位(雙精度)浮點數值狡相,如IEEE 754 標準中所描述梯轻。
int
和 double
是 num
的子類型。num類型包含基本運算符如 +, -, / 和 *尽棕,也包含其它方法中的 abs()
,ceil()
和 floor()
喳挑。(位相關的運算符,如 >>滔悉,在 int
類中定義)伊诵。 如果num及其子類型中沒有你所要尋找的,可以使用 dart:math 庫回官。
整型是不包含小數點的數字曹宴。以下是一些定義整型字面量的示例:
var x = 1;
var hex = 0xDEADBEEF;
如果數值中包含小數點,則為double類型歉提。以下是一些定義double字面量的示例:
var y = 1.1;
var exponents = 1.42e5;
在 Dart 2.1中浙炼,整型字面在需要時會自動轉化為double值:
double z = 1; // 等價于 z = 1.0.
Version note: 在Dart 2.1之前,在double上下文中使用整型字面量會報錯唯袄。
以下是如何將字符串轉化為數字及其反向操作:
// String -> int
var one = int.parse('1');
assert(one == 1);
// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);
// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');
int類型指定了傳統(tǒng)的按鈕移動 (<<, >>)、與 (&)和 或 (|)運算符蜗帜。例如:
assert((3 << 1) == 6); // 0011 << 1 == 0110
assert((3 >> 1) == 1); // 0011 >> 1 == 0001
assert((3 | 4) == 7); // 0011 | 0100 == 0111
字面量數字是編譯時常量恋拷。很多算術表達式也是編譯時常量,只要它們的運算項是運算為數值的編譯時常量厅缺。
const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;
字符串
Dart字符串是一個UTF-16代碼單元蔬顾。你可以使用單引號或雙引號來創(chuàng)建字符串:
var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";
可以通過使用${
expression
}
來將表達式的值放到字符串中。如果表達式是一個標識符湘捎,可以省略{}诀豁。要獲取一個對象對應的字符串,Dart中調用對象的toString()
方法窥妇。
var s = 'string interpolation';
assert('Dart has $s, which is very handy.' ==
'Dart has string interpolation, ' +
'which is very handy.');
assert('That deserves all caps. ' +
'${s.toUpperCase()} is very handy!' ==
'That deserves all caps. ' +
'STRING INTERPOLATION is very handy!');
Note: ==
運算符用于測試兩個對象是否相等舷胜。如果兩個字符串擁有相同序列的代碼單元則相等。
可以使用相鄰字符串字面量或者了 +
運算符來拼接字符串:
var s1 = 'String '
'concatenation'
" works even over line breaks.";
assert(s1 ==
'String concatenation works even over '
'line breaks.');
var s2 = 'The + operator ' + 'works, as well.';
assert(s2 == 'The + operator works, as well.');
另一種創(chuàng)建多行字符串的方式是使用三個單引號或三個雙引號標記:
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
可以通過r
前綴來創(chuàng)建“原生”字符串:
var s = r'In a raw string, not even \n gets special treatment.';
參見 Rune 來獲取如何在字符串中表達Unicode字符的詳情活翩。
字符串字面量是編譯時常量烹骨,僅需編譯時常量中的插值表達式運行結果為null或數值、字符串或布爾值材泄。
// 這些可以在一個const字符串中使用
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// 這些不可以在一個const字符串中使用
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];
const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';
更多有關使用字符串的信息沮焕,參見 字符串和正則表達式。
布爾型
要表現布爾值拉宗,Dart中有一個名為bool
的類型峦树。僅有兩個對象的類型為bool:布爾型字面量true
和 false
辣辫,它們都是編譯時常量。
Dart的類型安全意味著你不能使用if (*nonbooleanValue*)
或 assert (*nonbooleanValue*)
這樣的代碼魁巩。而是要顯式的檢查值急灭,類似這樣:
// 檢查空字符串
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);
列表
或許在幾乎所有編程語言中最常見的集合(collection)都是數組,或有序的對象組歪赢。在Dart化戳,數組是List對象,因此大部分會稱其為列表埋凯。
Dart的列表字面量和JavaScript中的數組字面量很像点楼。以下是一個簡單的Dart列表:
var list = [1, 2, 3];
Note: Dart推導 list
的類型為 List<int>
。如果你嘗試向這個列表中添加非整型對象白对,分析器或運行時會拋出錯誤掠廓。更多信息請閱讀 類型推導。
列表使用基于0的索引甩恼,即0是第1個元素的索引蟀瞧,并且 list.length - 1
是最后一個元素的索引。你可以完全像JavaScript中那樣獲取一個列表的長度并引用列表元素:
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);
要創(chuàng)建一個為運行是常量的列表条摸,在列表字面量之前添加 const
:
var constantList = const [1, 2, 3];
// constantList[1] = 1; // 取消這行的注釋會導致報錯
Dart 2.3 引入了展開運算符(...
) 及判空展開運算符 (...?
)悦污,提供一種向集合插入多個元素的簡潔方式。
例如钉蒲,你可以使用展開運算符 (...
) 來將列表中的所有元素插入到另一個列表中:
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
如果展開運算符右側的表達式有可能為null切端,可通過使用可判空展開運算符(...?
)來避免異常:
var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
更多有關使用展開運算符的詳情和示例,參見展開運算符提議顷啼。
Dart 2.3 還引入了collection if 和 collection for踏枣,可以使用條件(if
)和循環(huán) (for
)來構建使用條件(if
)和循環(huán) (for
)的集合。
以下是使用collection if來創(chuàng)建一個包含3項或4項的列表::
var nav = [
'Home',
'Furniture',
'Plants',
if (promoActive) 'Outlet'
];
以下是使用collection for來在將它們添加到另一個列表之前操作列表項的示例:
var listOfInts = [1, 2, 3];
var listOfStrings = [
'#0',
for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
更多有關使用collection if和for的示例钙蒙,參見 控制流collection建議茵瀑。
列表類型有很多操作列表的便捷的方法。有關列表更多的信息躬厌,請參見泛型 和 集合.
集
Dart中的集(set)是一個包含獨立項的無序集合马昨。Dart對集的支持由集字面量和Set類型所提供。
Version note: 雖然Set類型一直是Dart的核心部分扛施,集字面量在Dart 2.2中才引入偏陪。
以下是一個簡單的Dart集,使用集字面量創(chuàng)建:
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
Note: Dart推導 halogens
的類型為 Set<String>
煮嫌。 如果你嘗試向集添加錯誤類型的值笛谦,分析器或運行時會拋出錯誤。更多信息昌阿,請閱讀類型推導部分饥脑。
要創(chuàng)建空集恳邀,使用帶有類型前綴的{}
或向類型為Set
的變量賦值{}
:
var names = <String>{};
// Set<String> names = {}; // 這也同樣起作用
// var names = {}; // 創(chuàng)建一個映射,而非集
集或映射? 映射字面量的語法類型集字面量灶轰。因為映射字面量的優(yōu)先級更高谣沸, {}
的默認為 Map
類型。如果你忘記對 {}
進行類型標注笋颤,或者它所賦值的變量乳附,那么Dart會創(chuàng)建一個類型為 Map<dynamic, dynamic>
的對象。
使用add()
或addAll()
方法向已有集添加子項:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
使用 .length
來獲取集中子項的數量:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);
創(chuàng)建為運行時常量的集伴澄,在集字面量前添加const
:
final constantSet = const {
'fluorine',
'chlorine',
'bromine',
'iodine',
'astatine',
};
// constantSet.add('helium'); // 取消本行注釋會導致報錯
在Dart 2.3中赋除,集也像列表一樣支持展開運算符 (...
和 ...?
) 以及 collection if 和 for。更多信息非凌,參見 列表展開運算符 和 列表集合運算符 部分的討論举农。
映射
總的來說敞嗡,映射是一個關聯鍵和值的對象颁糟。鍵和值都可以為任意對象類型。每個鍵僅出現一次喉悴,但相同值可出現多次棱貌。Dart對映射的支持由映射字面量和 Map 類型所提供。
以下是一些簡單的Dart映射箕肃,使用映射字面量創(chuàng)建:
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
Note: Dart推導 gifts
的類型為 Map<String, String>
键畴,而nobleGases
的類型為 Map<int, String>
。如果你嘗試對任一映射添加錯誤類型的值突雪,分析器或運行時會拋出錯誤。更多相關信息涡贱,請參閱 類型推導咏删。
可以使用Map構造函數創(chuàng)建相同的對象:
var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
Note: 你可能會希望看到 new Map()
而非僅僅是 Map()
。從Dart 2中问词,關鍵字 new
為可選部分督函。更多詳情,參見使用構造函數激挪。
和JavaScript中一樣向已有映射添加鍵值對:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // 添加一個鍵值對
以JavaScript同樣的方式從映射中接收值:
var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');
如果你在尋找一個不在映射中的鍵辰狡,會在返回中獲取到null:
var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);
使用 .length
來獲取映射中鍵值對的數量:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
創(chuàng)建為編譯時常量的映射,在映射字面量之前添加const
:
final constantMap = const {
2: 'helium',
10: 'neon',
18: 'argon',
};
// constantMap[2] = 'Helium'; // 取消本行注釋會導致報錯
在Dart 2.3中垄分,映射像列表一樣支持展開運算符 (...
和 ...?
) 及collection if 和 for宛篇。有關詳情及示例,參見 展開運算符提議 和 控制流集合提議薄湿。
Runes
Dart中偷卧,runes是字符串的UTF-32代碼點。
Unicode為全球書寫系統(tǒng)中的每個字母吆倦、數字和符號定義了一個獨立的數值听诸。因為Dart字符串是一個UTF-16代碼單元的序列,在字符串中表達32位Unicode值要求有特殊的語法蚕泽。
通常表達一個Unicode代碼點的方式為 \uXXXX
晌梨,其中XXXX是一個4位數的16進制值。例如须妻,心形字符串 (?) 為 \u2665
仔蝌。要指定比4位16進制更多或更少的位數,需將值放在花括號中璧南。例如掌逛,笑臉emoji (??) 為 \u{1f600}
。
String 類有多個可用于提取rune信息的屬性司倚。 codeUnitAt
和 codeUnit
屬性返回16-位代碼單元豆混。使用 runes
屬性來獲取字符串的rune。
以下示例描述runes动知、16-位代碼單元以及32-位代碼單元之間的關系皿伺。點擊 Run 來實時查看rune。
Note: 在使用列表運算符操作runes時要小心盒粮。這種方法根據具體語言鸵鸥、字符集和操作可能會很容易崩潰。更多相關信息丹皱,參見Stack Overflow上的 如何倒排Dart中的字符串妒穴? 。
符號
符號(Symbol) 對象表示在Dart程序中聲明的一個運算符或標識符摊崭。你可能永遠都不會需要用到符號讼油,但它們對于通過名稱引用標識符的 API 有無限的價值,因為最小化會修改標識符的名稱但不修改標識符符號呢簸。
使用符號字面量獲取一個標識符的符號矮台,它只是#
后接標識符。
#radix
#bar<code class="language-nocode">
符號字面量是編譯時常量根时。
函數
Dart是一個真面向對象語言瘦赫,因此即使是函數也是對象,并且擁有一個類型Function蛤迎。這表示函數可賦值給變量或作為參數傳遞給其它函數确虱。也可以調用將Dart類的實例看作函數來進行調用。更多詳情替裆,參見 可調用類蝉娜。
以下是實現一個函數的示例:
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
雖然高效Dart推薦 為公有 API 進行類型標注唱较,函數則省略類型時依然有效:
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
對于僅包含一個表達式的函數,可以使用簡寫語法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
=> *expr*
語法是{ return *expr*; }
的簡寫召川。 =>
標記有時被稱為箭頭語法南缓。
Note: 僅有一個表達式 – 而不是一條語句 – 可以出現在箭頭(=>)和分號(;)之間。例如荧呐,你不能在這里放置一條 if語句汉形, 條件表達式。
函數可以有兩種類型的參數:必選和可選倍阐。必選參數放在前面概疆,后接可選參數》逄拢可選參數可為命名參數或位置參數岔冀。
Note: 有些API – 尤其是Flutter widget構造函數 – 即使用對改造參數也僅使用命名參數。參見下面一節(jié)了解更多詳情概耻。
可選參數
可選參數可以為命名參數或位置參數使套,或者是兩者都有。
命名參數
在調用一個函數時鞠柄,你可以使用*paramName*: *value*
指定命名參數侦高。例如:
enableFlags(bold: true, hidden: false);
在定義函數時,使用 {*param1*, *param2*, …}
來指定命名參數:
/// 設置bold 和 hidden標記 ...
void enableFlags({bool bold, bool hidden}) {...}
雖然命名參數是一種可選參數厌杜,通過可以@required 來標記它們以表示該參數是必須 – 即用戶必須為該參數提供一個值奉呛。例如:
const Scrollbar({Key key, @required Widget child})
如果有人嘗試不指定child
參數就創(chuàng)建一個Scrollbar
,那么分析器會報出問題夯尽。
使用@required 標注瞧壮,需要依賴meta 包并導入 package:meta/meta.dart
。
位置參數
將一組函數參數封裝在 []
中匙握,會將它們標記為可選位置參數:
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
以下是無添加可選參數調用該函數的示例:
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
以下是使用了第3個參數調用該函數的示例:
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
默認參數值
函數可以使用 =
來為命名參數和位置參數定義默認值咆槽。默認值必須為編譯時常量标沪。如未提供默認值寿桨,則默認值為 null
筐咧。
以下是一個為命名參數設置默認值的示例:
/// 設置bold 和 hidden標記 ...
void enableFlags({bool bold = false, bool hidden = false}) {...}
// bold將為true; hidden將為false
enableFlags(bold: true);
Deprecation note: 老代碼中可能會使用冒號 (:
) 代替 =
來設置命名函數的默認值。原因是原來命名函數中僅支持 :
赠堵。該支持可能會被淘汰,所以我們推薦使 用=
來指定默認值法褥。
下面的示例展示如何為位置參數設置默認值:
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;
}
assert(say('Bob', 'Howdy') ==
'Bob says Howdy with a carrier pigeon');
也可以傳遞列表或映射作為默認值茫叭。以下示例定義了一個函數doStuff()
,它為 list
參數指定了一個默認列表并為 gifts
參數指定了一個默認映射半等。
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');
}
main()函數
每個應用必須有一個頂級的 main()
函數揍愁,它作為應用的入口呐萨。 main()
函數返回 void
并有一個可選參數l List<String>
。
以下是一個針對web應用的 main()
函數示例:
void main() {
querySelector('#sample_text_id')
..text = 'Click me!'
..onClick.listen(reverseText);
}
Note: 以下代碼中 ..
語法稱作一個 級聯莽囤。通過級聯谬擦,你可以對單個對象的成員執(zhí)行多個操作。
以下是一個針對接收參數的命令行應用的main()
函數示例:
// 像這樣運行它: dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
可以使用 args庫 來定義和解析命令行參數朽缎。
函數作為一等對象
你可以傳遞函數來作為另一個函數的參數惨远。例如:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// 傳遞 printElement作為參數
list.forEach(printElement);
可將函數賦值給變量,如:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
該例使用了匿名函數话肖,更多相關內容請見下一節(jié)北秽。
匿名函數
大部分函數都是命名函數,如 main()
或 printElement()
最筒。你也可創(chuàng)建一個無名稱函數贺氓,稱為匿名函數,有時也稱為lambda 或閉包床蜘≌夼啵可以將匿名函數賦值給變量,那么例如就可以從集合中添加或刪除它悄泥。
匿名函數類似于命名函數 – 0或多個參數虏冻,在括號中由逗號和可選選類型標注分隔。
后面的代碼塊中包含函數體:
([[*Type*] *param1*[, …]]) { *codeBlock*; };
以下示例定義了一個帶有無類型參數 item
的匿名函數弹囚。對列表中的第一項所調用的函數厨相,打印包含在指定索引處的值的字符串。
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
點擊 Run 執(zhí)行下面的代碼鸥鹉。
如果函數僅包含一條語句蛮穿,可將其簡化為使用箭頭標記。將如下行拷貝到DartPad中并點擊Run來驗證它的功能是相同的毁渗。
list.forEach(
(item) => print('${list.indexOf(item)}: $item'));
詞法作用域
Dart是一個詞法作用域語言践磅,表示變量的作用域僅由代碼的布局靜態(tài)決定。你可以“按照外部花括號” 來查看變量是否在作用域中灸异。
以下是一個變量在各自作用域中嵌套函數的示例:
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
注意nestedFunction()
是如何使用每個級別的變量的府适,一路到頂級的變量。
詞法閉包
閉包是一個即使函數位置原作用域之外也可在詞法作用域中訪問變量的函數對象肺樟。
函數可封閉周圍作用域中所定義的變量檐春。在以下示例中, makeAdder()
捕獲變量 addBy
么伯。不論返回的函數放在哪里疟暖,它都能記住 addBy
。
/// 返回一個將addBy添加到函數參數中的函數
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void main() {
// 創(chuàng)建一個加2的函數
var add2 = makeAdder(2);
// 創(chuàng)建一個加4的函數
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
測試函數是否相等
下面是一個測試頂級函數、靜態(tài)方法和實例方法是否相等的示例:
void foo() {} // 一個頂級函數
class A {
static void bar() {} // 一個靜態(tài)方法
void baz() {} // 一個實例方法
}
void main() {
var x;
// 對比頂級函數
x = foo;
assert(foo == x);
// 對比靜態(tài)方法
x = A.bar;
assert(A.bar == x);
// 對比實例方法
var v = A(); // A 的實例 #1
var w = A(); // A 的實例 #2
var y = w;
x = w.baz;
// 這些閉包引用相同的實例 (#2),
// 因此它們相等俐巴。
assert(y.baz == x);
// 這些閉包引用不同的實例骨望,
// 因此它們不相等。
assert(v.baz != w.baz);
}
返回值
所有的函數都會返回值欣舵。如果未指定返回值擎鸠,語句 return null;
會隱式地附加到函數體中。
foo() {}
assert(foo() == null);
運算符
Dart定義了下表中顯示的運算符邻遏。你可以重載很多運算符糠亩,在重載運算符中進行了描述。
描述 | 運算符 | ||
---|---|---|---|
一元后置 |
*expr*++ *expr*-- () [] . ?.
|
||
一元前置 |
-*expr* !*expr* ~*expr* ++*expr* --*expr*
|
||
乘除 |
* / % ~/
|
||
加減 |
+ -
|
||
按位移 |
<< >> >>>
|
||
按位與 | & |
||
按位異或 | ^ |
||
按位或 | ` | ` | |
關系和類型測試 |
>= > <= < as is is!
|
||
等于 |
== !=
|
||
邏輯與 | && |
||
邏輯或 | ` | ` | |
判空(null) | ?? |
||
條件 | *expr1* ? *expr2* : *expr3* |
||
級聯 | .. |
||
賦值 |
= *= /= += -= &= ^= etc.
|
Warning: 運算符優(yōu)先級是對Dart解析器行為的估計准验。要獲取確定性的答案赎线,參見Dart語言規(guī)范中的語法。
在使用運算符時糊饱,可創(chuàng)建表達式垂寥。以下是一些運算符表達式的示例:
a++
a + b
a = b
a == b
c ? a : b
a is T
在運算符表格中, 每個運算符都比下面一行的運算符優(yōu)先級要高另锋。比如乘法運算符 %
的優(yōu)先級高于(因此先執(zhí)行)等號運算符 ==
滞项,而它的優(yōu)先級又高于邏輯與運算符 &&
。這個優(yōu)先級表示以下兩行代碼的執(zhí)行方式相同:
// 括號提升可讀性
if ((n % i == 0) && (d % i == 0)) ...
// 更驗閱讀夭坪,但是等價的
if (n % i == 0 && d % i == 0) ...
Warning: 對于使用兩個運算項的運算符文判,最左側的運算項決定使用哪個版本的運算符。例如室梅,如果有一個Vector對象和一個Point對象戏仓, aVector + aPoint
使用Vector版本的+。
算術運算符
Dart支持常見算術運算符亡鼠,如下表所示:
運算符 | 含義 |
---|---|
+ |
加 |
– |
減 |
-*expr* |
一元減赏殃,也稱為否定(反轉表達式的符號) |
* |
乘 |
/ |
除 |
~/ |
整除,返回一個整數結果 |
% |
獲取整數相除的余數(模) |
示例:
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // 結果是雙精度類型
assert(5 ~/ 2 == 2); // 結果是整型
assert(5 % 2 == 1); // 余數
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* ) |
示例:
var a, b;
a = 0;
b = ++a; // 在b獲取值之前對a遞增
assert(a == b); // 1 == 1
a = 0;
b = a++; // 在b獲取值之后對a遞增
assert(a != b); // 1 != 0
a = 0;
b = --a; // 在b獲取值之前對a遞減
assert(a == b); // -1 == -1
a = 0;
b = a--; // 在b獲取值之后對a遞減
assert(a != b); // -1 != 0
比較和關系運算符
下表中列出了比較和關系運算符的含義仁热。
運算符 | 含義 |
---|---|
== |
等于,參見下面的討論 |
!= |
不等于 |
> |
大于 |
< |
小于 |
>= |
大于等于 |
<= |
小于等于 |
測試兩個對象x 和 y 是否表示相同的內容勾哩,使用 ==
運算符抗蠢。(在極少的情況中,需要知道兩個對象是否是完全相同的對象思劳,轉而使用 identical()函數迅矛。) 以下展示 ==
運算符如何運行:
- 如果x 或 y 為null,如果兩者都為null則返回true敢艰,如果只一個為null則返回false诬乞。
- 返回
*x*.==(*y*)
方法運行的結果 册赛。(沒錯钠导,==
這樣的運算符是對它們的第一項所調用的方法震嫉。你甚至可以重載很多運算符,包括==
牡属, 可參見 重載運算符票堵。)
以下是使用每個比較和關系運算符的示例:
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
類型測試運算符
as
, is
和 is!
運算符對于在運行時檢查類型非常方便。
運算符 | 含義 |
---|---|
as |
對象類型轉換 (也用于指定 庫前綴) |
is |
如果對象有指定的類型時為true |
is! |
如果對象有指定的類型時為false |
如果obj
實現了T
所指定的接口的話則 obj is T
的結果為 true逮栅。例如悴势,obj is Object
永遠為true。
使用 as
運算符將對象轉換為指定類型措伐。通常特纤,應使用它作為對象is
測試的簡寫,后接使用該對象的表達式侥加。例如捧存,考慮使用如下代碼:
if (emp is Person) {
// 類型檢查
emp.firstName = 'Bob';
}
可以使用 as
運算符讓代碼更精簡:
(emp as Person).firstName = 'Bob';
Note: 兩種代碼并不等同。如果 emp
為null 或者不是Person担败,第1個例子(帶is
)不會做任何事昔穴,第二個(帶as
)拋出異常。
賦值運算符
如你所見提前,可以使用=
運算符進行賦值吗货。僅為值為null的變量賦值時,使用 ??=
運算符狈网。
// 為a賦值
a = value;
// 若b為null時為 b 賦值宙搬,否則,b 保持不變
b ??= value;
復合賦值運算符如 +=
將運算和賦值合并在一起孙援。
| =
| –=
| /=
| %=
| >>=
| ^=
|
| +=
| *=
| ~/=
| <<=
| &=
| |=
|
以下是復合賦值運算符運行的方式:
復合賦值 | 比較表達式 | |
---|---|---|
對于運算符 op: | a *op*= b |
a = a *op* b |
示例: | a += b |
a = a + b |
下例中使用賦值和復合賦值運算符:
var a = 2; // 使用 =賦值
a *= 3; // 賦值和乘法: a = a * 3
assert(a == 6);
邏輯運算符
可以使用邏輯運算符取反或合并布爾表達式害淤。
運算符 | 含義 | ||
---|---|---|---|
!*expr* |
對緊接著的表達式進行取反(修改false 為 true,反之亦然) | ||
` | ` | 邏輯或 | |
&& |
邏輯與 |
以下是一個使用邏輯運算符的示例:
if (!done && (col == 0 || col == 3)) {
// ...Do something...
}
按位及左移右移運算符
可以在Dart中操作數字的單個位拓售。通常窥摄,會對整型使用這些按位和移動運算符。
運算符 | 含義 | |
---|---|---|
& |
與 | |
` | ` | 或 |
^ |
異或 | |
~*expr* |
一元按位取反(0變成1础淤,1變成0) | |
<< |
左移 | |
>> |
右移 |
以下是使用按位和移動運算符的示例:
final value = 0x22;
final bitmask = 0x0f;
assert((value & bitmask) == 0x02); // 與
assert((value & ~bitmask) == 0x20); // 與 取反
assert((value | bitmask) == 0x2f); // 或
assert((value ^ bitmask) == 0x2d); // 異或
assert((value << 4) == 0x220); // 左移
assert((value >> 4) == 0x02); // 右移
條件表達式
Dart有兩個運算符可以讓我們簡潔地運行可能會要求使用 if-else 語句的表達式:
*condition* ? *expr1* : *expr2*
若if條件為true, 運行 expr1 (并返回它的值)崭放,否則運行并返回 expr2的值。
*expr1* ?? *expr2*
右 expr1 為非空鸽凶,返回其值币砂,否則運行并返回 expr2的值。
在需要根據布爾表達式進行賦值時玻侥,考慮使用 ?:
决摧。
var visibility = isPublic ? 'public' : 'private';
如果要測試布爾表達式是否為null,考慮使用 ??
。
String playerName(String name) => name ?? 'Guest';
前例至少可通過另外兩種方式進行編寫掌桩,但都沒有它簡潔:
// 使用?: 運算符的稍長版本
String playerName(String name) => name != null ? name : 'Guest';
// 使用if-else語句的非常長的版本
String playerName(String name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
}
級聯標記 (..)
級聯 (..
) 允許我們對同一對象進行一系列操作边锁。除調用函數外,你還可以對相同的對象訪問字段波岛。這通趁┨常可節(jié)省創(chuàng)建臨時變量的步驟并讓我們能編寫列流暢的代碼。
思考如下代碼:
querySelector('#confirm') // 獲取一個對象
..text = 'Confirm' // 使用它的成員
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
第1個方法調用querySelector()
则拷,返回一個選擇器對象贡蓖。緊接級聯標記的代碼對選擇器對象進行操作,它忽略所有后續(xù)可能返回的值煌茬。
前例等同于:T
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')
..write('bar'); // Error: method 'write' isn't defined for 'void'.
sb.write()
調用返回void,而無法對 void
構建級聯浑吟。
Note: 嚴格的說笙纤,對級聯使用的“雙點號”標記并非運算符。它只是Dart語法的一部分组力。
其它運算符
我們已經在其它示例中看到過大部分剩余的運算符:
運算符 | 名稱 | 含義 |
---|---|---|
() |
函數應用 | 表示一個函數調用 |
[] |
列表訪問 | 引用列表中所指定索引處的值 |
. |
成員訪問 | 引用一個表達式的屬性省容;例如:foo.bar 從表達式foo 中選擇屬性 bar 。 |
?. |
條件成員訪問 | 類似 . 燎字, 但最左側的運算項可以為null腥椒;例如: foo?.bar 從foo 中選擇屬性 bar ,除非 foo 為 null (這時 foo?.bar 的值為 null) |
更多有關.
, ?.
和 ..
運算符的信息候衍,參見 類笼蛛。
流程控制語句
可以使用以下方式來控制Dart 代碼中的流程
-
if
和else
-
for
循環(huán) -
while
和do
–while
循環(huán) -
break
和continue
-
switch
和case
assert
也可以使用try-catch
和 throw
來影響控制流程,在 異常中有進行講解蛉鹿。
if和else
Dart 支持帶有可選的else
語句的 if
語句滨砍,如下例中所示。同時請參見 條件表達式妖异。
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
不同于JavaScript惋戏,條件必須使用布爾值,其它的都不行他膳。參見 布爾型 獲取更多信息响逢。
for循環(huán)
你可以使用標準for
循環(huán)進行迭代。例如:
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
message.write('!');
}
Dart for
循環(huán)中的裝飾捕獲索引的值棕孙,避免JavaScript中所發(fā)現的常見問題舔亭。例如些膨,思考:
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
會如所預期的先輸出 0
再輸出 1
。但對比在JavaScript中則會先打印 2
再打印 2
钦铺。
如果所迭代的對象是一個 Iterable傀蓉,可以使用forEach() 方法。如果無需知道當前的迭代計數器的話使用 forEach()
是一個很好的選擇:
candidates.forEach((candidate) => candidate.interview());
Iterable類如List 和 Set 還把持迭代的 for-in
形式:
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
while和do-while
while
循環(huán)在循環(huán)之前運行條件:
while (!isDone()) {
doSomething();
}
do
–while
在循環(huán)之后運行條件:
do {
printLine();
} while (!atEndOfPage());
break和continue
使用 break
來停止循環(huán):
while (true) {
if (shutDownRequested()) break;
processIncomingRequests();
}
使用 continue
來跳至下一次迭代:
for (int i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.yearsExperience < 5) {
continue;
}
candidate.interview();
}
如果在使用列表或集這樣的Iterable時可以會以不同的方式編寫該示例:
candidates
.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
switch和case
Dart中的switch 語句使用==
比較整型职抡、字符串或編譯時常量。 比較的對象必須都是相同類的實例(而非其子類型的)误甚,并且該類不能重載 ==
.缚甩。枚舉類型 在 switch
語句中可以良好運行。
Note: Dart中的switch語句針對受限的環(huán)境中窑邦,如解釋器或掃描器擅威。
每個非空 case
從句按照規(guī)則以break
語句結束。其它結束非空 case
從名的有效方式有 continue
, throw
或 return
語句冈钦。
在沒有匹配的case
從句時使用 default
從句來執(zhí)行代碼:
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
break;
case 'PENDING':
executePending();
break;
case 'APPROVED':
executeApproved();
break;
case 'DENIED':
executeDenied();
break;
case 'OPEN':
executeOpen();
break;
default:
executeUnknown();
}
下例在一個case
從句中省略了 break
語句郊丛,因此產生了一個報錯:
var command = 'OPEN';
switch (command) {
case 'OPEN':
executeOpen();
// ERROR: Missing break
case 'CLOSED':
executeClosed();
break;
}
但是,Dart確實支持空的 case
從句允許越過的形式:
var command = 'CLOSED';
switch (command) {
case 'CLOSED': // 空的case直接越過
case 'NOW_CLOSED':
// 同時對CLOSED 和 NOW_CLOSED運行
executeNowClosed();
break;
}
如果你真的希望越過瞧筛,可以使用continue
語句及一個標簽:
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// 繼續(xù)在nowClosed標簽處執(zhí)行
nowClosed:
case 'NOW_CLOSED':
// 對CLOSED 和 NOW_CLOSED同時運行
executeNowClosed();
break;
}
case
從句可以有局部變量厉熟,僅在從句作用域內部可見。
斷言
在開發(fā)過程中较幌,可以使用斷言語句 assert(*condition*, *optionalMessage*)
; 來在布爾條件為false時打斷正常的執(zhí)行揍瑟。可以通過本導覽找到斷言語句的很多示例乍炉。以下是另外一些示例:
// 確保變量值為非null
assert(text != null);
// 確保值小于100
assert(number < 100);
// 確保這是一個https鏈接
assert(urlString.startsWith('https'));
要為斷言關聯消息绢片,對assert
添加一個字符串作為第2個參數。
assert(urlString.startsWith('https'),
'URL ($urlString) should start with "https".');
assert
的第1個參數可以是解析為布爾值的任意表達式岛琼。如果表達式的值為true底循,斷言成功且執(zhí)行繼續(xù)。如果它為false槐瑞,斷言失敗且會拋出異常(一個AssertionError)熙涤。
斷言到底做了什么呢?這取決于你所使用的工具和框架:
- Flutter在調試模式中啟用斷言困檩。
- 僅針對開發(fā)的工具如 dartdevc 通常默認啟用斷言灭袁。
- 一些工具,如 dart 和 dart2js窗看,支持通過命令行標記的斷言:
--enable-asserts
.
在生產模式下茸歧,會忽略斷言, assert
的參數不會被執(zhí)行显沈。
異常
Dart代碼可以拋出和捕獲異常软瞎。異常是表示預期外的事情發(fā)生時的報錯逢唤。如未捕獲到異常,拋出異常的 隔離(isolate)被掛起涤浇,并且通常隔離及其程序會被終止鳖藕。
不同睛Java,Dart的異常都是非檢查型異常只锭。方法未聲明它們所要拋出的異常著恩,那么也不要求你捕獲任何異常。
Dart提供 Exception 和 Error 類型蜻展,以及很多預定義的子類型喉誊。當然也可以定義自己的異常。但是Dart程序可拋出任意非空對象(不只是Exception和Error對象)來作為異常纵顾。
Throw
以下是一個拋出異常的示例:
throw FormatException('Expected at least 1 section');
你也可以拋出自己的對象:
throw 'Out of llamas!';
Note: 生產質量的代碼通常拋出實現Error 或 Exception的類型伍茄。
因為拋出異常是一個表達式,你可以在=>及其它允許使用表達式的任何地方拋出異常:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
處理會拋出一個類型以上異常的代碼施逾,可以指定多個捕獲分分鐘敷矫。第一個匹配所拋出對象類型的catch從句會處理該異常。如果catch從句未指定類型汉额,該分分鐘可處理任意類型拋出的對象:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 一個具體異常
buyMoreLlamas();
} on Exception catch (e) {
// 任何其它異常
print('Unknown exception: $e');
} catch (e) {
// 無具體類型曹仗,處理所有類型
print('Something really unknown: $e');
}
如以上代表所示,可以使用 on
或 catch
或者同時使用蠕搜。在需要指定異常類型時使用 on
整葡。在你的異常處理器需要異常對象時使用 catch
。
可以為catch()
指定一個或兩個參數讥脐。第1個是拋出的異常遭居,第二是棧跟蹤 (一個 StackTrace對象).
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
要部分處理異常,但允許其執(zhí)行旬渠,使用 rethrow
關鍵字俱萍。
void misbehave() {
try {
dynamic foo = true;
print(foo++); // 運行時錯誤
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // 允許調用者查看該異常
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
Finally
要確保一些代碼不管是否拋出異常時都執(zhí)行,使用 finally
從句告丢。如果沒有 catch
從句匹配該異常枪蘑,在finally
從句執(zhí)行后會推出異常。
try {
breedMoreLlamas();
} finally {
// 即使在沒有拋出異常時也會清理
cleanLlamaStalls();
}
finally
從任意匹配的catch
從句之后執(zhí)行:
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // 首先處理異常
} finally {
cleanLlamaStalls(); // 然后進行清理
}
閱讀庫導覽的 異常 的一節(jié)了解更多信息岖免。
類
Dart是一個帶有類和基于mixin繼承的面向對象語言岳颇。每個對象都是類的實例,所有類都繼承自 Object.颅湘。* 基于mixin的繼承* 表示雖然每個類(除Object外)都只有一個超類话侧,一個類主體可在多個類級別中復用。
使用類成員
對象有由函數和數據(分別為方法和實例變量)組成的成員闯参。在調用方法時瞻鹏,對一個對象進行調用:該方法可訪問對象的函數和數據悲立。
使用點號 (.
) 來引用于實例變量或方法:
var p = Point(2, 2);
// 設置實例變量y的值
p.y = 3;
// 獲取 y 的值
assert(p.y == 3);
// 對 p 調用 distanceTo()
num distance = p.distanceTo(Point(4, 4));
使用 ?.
代替 .
來避免最左側操作項為null時的異常:
// 如果p 不為null, 設置它的y 值為 4
p?.y = 4;
使用構造函數
可以使用構造函數創(chuàng)建對象。構造函數名可為 *ClassName*
或 *ClassName*.*identifier*
新博。例如薪夕,如下代碼使用Point()
和 Point.fromJson()
構造函數創(chuàng)建 Point
對象:
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
以下代碼有同樣的效果,但在構造函數名前使用可選的關鍵字 new
:
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
Version note: 關鍵字 new
Dart 2中成為可選項赫悄。
一些類提供 常量構造函數原献。要使用常量構造函數創(chuàng)建編譯時常量,在構造函數名前放置關鍵字 const
:
var p = const ImmutablePoint(2, 2);
構造兩個相同的編譯時常量會產生單個相同的實例:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // 它們是相同的實例埂淮!
在常量上下文中姑隅,可以在構造函數或字面量前省略 const
。例如同诫,查看如下創(chuàng)建一個const映射的代碼:
// 這里有很多const 關鍵字
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
可以省略掉第一次使用之外的所有 const
關鍵字:
// 僅一個const, 它創(chuàng)建常量上下文
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
如果常量構造函數在常量上下文之外且未使用 const
調用,它創(chuàng)建一個非常量對象樟澜。
var a = const ImmutablePoint(1, 1); // 創(chuàng)建一個常量
var b = ImmutablePoint(1, 1); // 不創(chuàng)建常量
assert(!identical(a, b)); // 不是相同的實例误窖!
Version note: 關鍵字 const
在Dart 2的常量上下文成為可選。
獲取一個對象的類型
要在運行時獲取對象的類型秩贰,可以使用Object的 runtimeType
屬性霹俺,它返回一個 Type 對象。
print('The type of a is ${a.runtimeType}');
到這里毒费,你已學習了如何使用類丙唧。本節(jié)后面的部分展示如何實現類。
實例變量
以下是如何聲明實例變量:
class Point {
num x; // 聲明實例變量 x, 初始值為 null
num y; // 聲明 y, 初始值為 null
num z = 0; // 聲明 z, 初始值為 0
}
所有未初始化的實例變量的值為 null
觅玻。
所有的實例變量生成一個getter 方法想际。非final實例變量也生成一個隱式setter 方法。更多詳情溪厘,參見getters和setters胡本。
class Point {
num x;
num y;
}
void main() {
var point = Point();
point.x = 4; // 使用針對x 的setter方法
assert(point.x == 4); // 使用針對x 的getter方法
assert(point.y == null); // 默認值為null
}
如果在其聲明處實例化實例變量(而非在構造函數或方法中進行),在實例創(chuàng)建時設置值畸悬,即在構造函數及期初始化程序列表執(zhí)行前侧甫。
本文首發(fā)地址:Alan Hou的個人博客