參考網(wǎng)址 http://dart.goodev.org/guides/language/language-tour 相關(guān)數(shù)據(jù)所有權(quán)歸該網(wǎng)站所有,當(dāng)前文章不會產(chǎn)生收益。
該文章不會被標(biāo)注被原創(chuàng),如果其他人復(fù)制或轉(zhuǎn)載或以其他方式獲取當(dāng)前文章內(nèi)容并標(biāo)注為原創(chuàng),由此與原作者產(chǎn)生的糾紛與本人無關(guān)。
文章將會被同步至微信公眾號:Android部落格,也不會被標(biāo)注為原創(chuàng)。
1、Variables(變量)
var name = 'Bob';
變量是一個引用。上面名字為 name 的變量引用了 一個內(nèi)容為 “Bob” 的 String 對象。
- 1)Default value(默認值)
沒有初始化的變量自動獲取一個默認值為 null。類型為數(shù)字的 變量如何沒有初始化其值也是 null,不要忘記了 數(shù)字類型也是對象。
int lineCount;
assert(lineCount == null);
2、Optional types(可選的類型)
在聲明變量的時候,你可以選擇加上具體 類型:
String name = 'Bob';
添加類型可以更加清晰的表達你的意圖
3、Final and const
如果你以后不打算修改一個變量,使用 final 或者 const。 一個 final 變量只能賦值一次;一個 const 變量是編譯時常量。(Const 變量同時也是 final 變量。) 頂級的 final 變量或者類中的 final 變量在 第一次使用的時候初始化。
注意: 實例變量可以為 final 但是不能是 const 。
下面是 final 變量的示例:
final name = 'Bob'; // Or: final String name = 'Bob';
const 變量為編譯時常量。 如果 const 變量在類中,請定義為 static const。 可以直接定義 const 和其值,也 可以定義一個
const var message = new StringBuffer("Dart is fun");
for (var i = 0; i < 5; i++) {
message.write('!');
}
變量使用其他 const 變量的值來初始化其值。
const bar = 1000000; // Unit of pressure (dynes/cm2)
const atm = 1.01325 * bar; // Standard atmosphere
const 關(guān)鍵字不僅僅只用來定義常量。 有可以用來創(chuàng)建不變的值, 還能定義構(gòu)造函數(shù)為 const 類型的,這種類型 的構(gòu)造函數(shù)創(chuàng)建的對象是不可改變的。任何變量都可以有一個不變的值。
// Note: [] creates an empty list.
// const [] creates an empty, immutable list (EIA).
var foo = const []; // foo is currently an EIA.
final bar = const []; // bar will always be an EIA.
const baz = const []; // baz is a compile-time constant EIA.
// You can change the value of a non-final, non-const variable,
// even if it used to have a const value.
foo = [];
// You can't change the value of a final or const variable.
// bar = []; // Unhandled exception.
// baz = []; // Unhandled exception.
4、Built-in types(內(nèi)置的類型)
Dart 內(nèi)置支持下面這些類型:
- numbers
- strings
- booleans
- lists (也被稱之為 arrays)
- maps
- runes (用于在字符串中表示 Unicode 字符)
- symbols
你可以直接使用字母量來初始化上面的這些類型。 例如 'this is a string' 是一個字符串字面量, true 是一個布爾字面量。
由于 Dart 中每個變量引用的都是一個對象 – 一個類的實例, 你通常使用構(gòu)造函數(shù)來初始化變量。
一些內(nèi)置的類型具有自己的構(gòu)造函數(shù)。例如, 可以使用 Map()構(gòu)造函數(shù)來創(chuàng)建一個 map, 就像這樣 new Map()。
1)Numbers(數(shù)值)
Dart 支持兩種類型的數(shù)字:
- int
整數(shù)值,其取值通常位于 -253 和 253 之間。 - double
64-bit (雙精度) 浮點數(shù),符合 IEEE 754 標(biāo)準。
int 和 double 都是 num 的子類。
num 類型定義了基本的操作符,例如 +, -, /, 和 *, 還定義了 abs()、 ceil()、和 floor() 等 函數(shù)。
整數(shù)是不帶小數(shù)點的數(shù)字。下面是一些定義 整數(shù)的方式:
var x = 1;
var hex = 0xDEADBEEF;
var bigInt = 34653465834652437659238476592374958739845729;
如果一個數(shù)帶小數(shù)點,則其為 double, 下面是定義 double 的一些方式:
var y = 1.1;
var exponents = 1.42e5;
下面是字符串和數(shù)字之間轉(zhuǎn)換的方式:
// 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');
整數(shù)類型支持傳統(tǒng)的位移操作符,(<<, >>), AND (&), 和 OR (|) 。例如:
assert((3 << 1) == 6); // 0011 << 1 == 0110
assert((3 >> 1) == 1); // 0011 >> 1 == 0001
assert((3 | 4) == 7); // 0011 | 0100 == 0111
數(shù)字字面量為編譯時常量。 很多算術(shù)表達式 只要其操作數(shù)是常量,則表達式結(jié)果 也是編譯時常量:
const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;
2)Strings(字符串)
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}。如果表達式是一個標(biāo)識符,可以省略 {}。 如果表達式的結(jié)果為一個對象,則 Dart 會調(diào)用對象的 toString() 函數(shù)來獲取一個字符串。
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!');
注意: == 操作符判斷兩個對象的內(nèi)容是否一樣。
如果兩個字符串包含一樣的字符編碼序列, 則他們是相等的。
可以使用 + 操作符來把多個字符串鏈接為一個,也可以把多個 字符串放到一起來實現(xiàn)同樣的功能:
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)建一個 “原始 raw” 字符串:
var s = r"In a raw string, even \n isn't special.";
字符串字面量是編譯時常量, 帶有字符串插值的字符串定義,若干插值表達式引用的為編譯時常量則其結(jié)果也是編譯時常量:
// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = const [1, 2, 3];
const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';
3)Booleans(布爾值)
為了代表布爾值,Dart 有一個名字為 bool 的類型。
只有兩個對象是布爾類型的:true 和 false 所創(chuàng)建的對象, 這兩個對象也都是編譯時常量。
當(dāng) Dart 需要一個布爾值的時候,只有 true 對象才被認為是 true。 所有其他的值都是 flase。
Dart 這樣設(shè)計布爾值,是為了避免奇怪的行為。很多 JavaScript 代碼 都遇到這種問題。
對于你來說,在寫代碼的時候你不用這些寫代碼:if (nonbooleanValue),你應(yīng)該顯式的 判斷變量是否為布爾值類型。
例如:
// Check for an empty string.
var fullName = '';
assert(fullName.isEmpty);
// Check for zero.
var hitPoints = 0;
assert(hitPoints <= 0);
// Check for null.
var unicorn;
assert(unicorn == null);
// Check for NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);
4)Lists(列表)
也許 array (或者有序集合)是所有編程語言中最常見的集合類型。 在 Dart 中數(shù)組就是 List 對象。所以 通常我們都稱之為 lists。
Dart list 字面量和 JavaScript 的數(shù)組字面量類似。下面是一個 Dart list 的示例:
var list = [1, 2, 3];
Lists 的下標(biāo)索引從 0 開始,第一個元素的索引是 0. list.length - 1 是最后一個元素的索引。 訪問 list 的長度和元素與 JavaScript 中的用法一樣:
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);
在 list 字面量之前添加 const 關(guān)鍵字,可以 定義一個不變的 list 對象(編譯時常量):
var constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.
5)Maps
通常來說,Map 是一個鍵值對相關(guān)的對象。 鍵和值可以是任何類型的對象。每個 鍵 只出現(xiàn)一次, 而一個值則可以出現(xiàn)多次。Dart 通過 map 字面量 和 Map 類型支持 map。
下面是一些創(chuàng)建簡單 map 的示例:
var gifts = {
// Keys Values
'first' : 'partridge',
'second': 'turtledoves',
'fifth' : 'golden rings'
};
var nobleGases = {
// Keys Values
2 : 'helium',
10: 'neon',
18: 'argon',
};
使用 Map 構(gòu)造函數(shù)也可以實現(xiàn)同樣的功能:
var gifts = new Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = new Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
往 map 中添加新的鍵值對和在 JavaScript 中的用法一樣:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair
獲取 map 中的對象也和 JavaScript 的用法一樣:
var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');
如果所查找的鍵不存在,則返回 null:
var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);
使用 .length 來獲取 map 中鍵值對的數(shù)目:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
同樣使用 const 可以創(chuàng)建一個 編譯時常量的 map:
final constantMap = const {
2: 'helium',
10: 'neon',
18: 'argon',
};
// constantMap[2] = 'Helium'; // Uncommenting this causes an error.
6)Runes
在 Dart 中,runes 代表字符串的 UTF-32 code points。
Unicode 為每一個字符、標(biāo)點符號、表情符號等都定義了 一個唯一的數(shù)值。
由于 Dart 字符串是 UTF-16 code units 字符序列, 所以在字符串中表達 32-bit Unicode 值就需要 新的語法了。
通常使用 \uXXXX 的方式來表示 Unicode code point, 這里的 XXXX 是4個 16 進制的數(shù)。 例如,心形符號 (?) 是 \u2665。 對于非 4 個數(shù)值的情況, 把編碼值放到大括號中即可。 例如,笑臉 emoji (??) 是 \u{1f600}。
String 類 有一些屬性可以提取 rune 信息。 codeUnitAt 和 codeUnit 屬性返回 16-bit code units。 使用 runes 屬性來獲取字符串的 runes 信息。
下面是示例演示了 runes、 16-bit code units、和 32-bit code points 之間的關(guān)系。
main() {
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
}
結(jié)果是:
??
[55357, 56399]
[128079]
? ?? ?? ?? ?? ??
7)Symbols
一個 Symbol object 代表 Dart 程序中聲明的操作符或者標(biāo)識符。
你也許從來不會用到 Symbol,但是該功能對于通過名字來引用標(biāo)識符的情況 是非常有價值的,特別是混淆后的代碼, 標(biāo)識符的名字被混淆了,但是 Symbol 的名字不會改變。
使用 Symbol 字面量來獲取標(biāo)識符的 symbol 對象,也就是在標(biāo)識符 前面添加一個 # 符號:
#radix
#bar
Symbol 字面量定義是編譯時常量。
5、Functions(方法)
Dart 是一個真正的面向?qū)ο笳Z言,方法也是對象并且具有一種 類型, Function。
這意味著,方法可以賦值給變量,也可以當(dāng)做其他方法的參數(shù)。 也可以把 Dart 類的實例當(dāng)做方法來調(diào)用。
下面是定義方法的示例:
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
當(dāng)然也可以選擇忽略類型定義:
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
對于只有一個表達式的方法,你可以選擇 使用縮寫語法來定義:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
這個 => expr 語法是 { return expr; } 形式的縮寫。=> 形式 有時候也稱之為 胖箭頭 語法。
注意: 在箭頭 (=>) 和冒號 (;) 之間只能使用一個 表達式 – 不能使用 語句。 例如:你不能使用 if statement,但是可以 使用條件表達式conditional expression。
方法可以有兩種類型的參數(shù):必需的和可選的。 必需的參數(shù)在參數(shù)列表前面, 后面是可選參數(shù)。
1)Optional parameters(可選參數(shù))
可選參數(shù)可以是命名參數(shù)或者基于位置的參數(shù),但是這兩種參數(shù)不能同時當(dāng)做可選參數(shù)。
Optional named parameters(可選命名參數(shù))
調(diào)用方法的時候,你可以使用這種形式 paramName: value 來指定命名參數(shù)。例如:
enableFlags(bold: true, hidden: false)
在定義方法的時候,使用 {param1, param2, …} 的形式來指定命名參數(shù):
/// Sets the [bold] and [hidden] flags to the values
/// you specify.
enableFlags({bool bold, bool hidden}) {
// ...
}
2)Optional positional parameters(可選位置參數(shù))
把一些方法的參數(shù)放到 [] 中就變成可選 位置參數(shù)了:
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
下面是不使用可選參數(shù)調(diào)用上面方法 的示例:
assert(say('Bob', 'Howdy') == 'Bob says Howdy')
下面是使用可選參數(shù)調(diào)用上面方法的示例:
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
3)Default parameter values(默認參數(shù)值)
在定義方法的時候,可以使用 = 來定義可選參數(shù)的默認值。 默認值只能是編譯時常量。 如果沒有提供默認值,則默認值為 null。
下面是設(shè)置可選參數(shù)默認值的示例:
/// Sets the [bold] and [hidden] flags to the values you
/// specify, defaulting to false.
void enableFlags({bool bold = false, bool hidden = false}) {
// ...
}
// bold will be true; hidden will be false.
enableFlags(bold: true);
版本問題: 就版本代碼可能需要使用一個冒號 (:) 而不是 = 來設(shè)置參數(shù)默認值。 原因在于 Dart SDK 1.21 之前的版本,命名參數(shù)只支持 :。 :
設(shè)置命名默認參數(shù)值在以后版本中將不能使用, 所以我們推薦你 使用 = 來設(shè)置默認值, 并 指定 Dart SDK 版本為 1.21 或者更高的版本。
下面的示例顯示了如何設(shè)置位置參數(shù)的默認值:
String say(String from, String msg,
[String device = 'carrier pigeon', String mood]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
if (mood != null) {
result = '$result (in a $mood mood)';
}
return result;
}
assert(say('Bob', 'Howdy') ==
'Bob says Howdy with a carrier pigeon');
還可以使用 list 或者 map 作為默認值。 下面的示例定義了一個方法 doStuff(), 并分別為 list 和 gifts 參數(shù)指定了 默認值:
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');
}
6、The main() function(入口函數(shù))
每個應(yīng)用都需要有個頂級的 main() 入口方法才能執(zhí)行。 main() 方法的返回值為 void 并且有個可選的 List<String> 參數(shù)。
下面是一個 web 應(yīng)用的 main() 方法:
void main() {
querySelector("#sample_text_id")
..text = "Click me!"
..onClick.listen(reverseText);
}
注意: 前面代碼中的 .. 語法為 級聯(lián)調(diào)用(cascade)。 使用級聯(lián)調(diào)用語法, 你可以在一個對象上執(zhí)行多個操作。
下面是一個命令行應(yīng)用的 main() 方法,并且使用了 方法參數(shù)作為輸入?yún)?shù):
// Run the app like this: 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');
}
7、Functions as first-class objects(一等方法對象)
可以把方法當(dāng)做參數(shù)調(diào)用另外一個方法。例如:
printElement(element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
方法也可以賦值給一個變量:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
8、Anonymous functions(匿名方法)
大部分方法都帶有名字,例如 main() 或者 printElement()。 你有可以創(chuàng)建沒有名字的方法,稱之為 匿名方法,有時候也被稱為 lambda 或者 closure 閉包。 你可以把匿名方法賦值給一個變量, 然后你可以使用這個方法,比如添加到集合或者從集合中刪除。
匿名函數(shù)和命名函數(shù)看起來類似— 在括號之間可以定義一些參數(shù),參數(shù)使用逗號 分割,也可以是可選參數(shù)。 后面大括號中的代碼為函數(shù)體:
([[Type] param1[, …]]) {
codeBlock;
};
下面的代碼定義了一個參數(shù)為i (該參數(shù)沒有指定類型)的匿名函數(shù)。 list 中的每個元素都會調(diào)用這個函數(shù)來 打印出來,同時來計算了每個元素在 list 中的索引位置:
var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
print(list.indexOf(i).toString() + ': ' + i);
});
如果方法只包含一個語句,可以使用胖箭頭語法縮寫。 把下面的代碼粘貼到 DartPad 中運行,可以看到結(jié)果是一樣的:
list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
9、Lexical scope(靜態(tài)作用域)
Dart 是靜態(tài)作用域語言,變量的作用域在寫代碼的時候就確定過了。
基本上大括號里面定義的變量就 只能在大括號里面訪問,和 Java 作用域 類似。
下面是作用域的一個 示例:
var topLevel = true;
main() {
var insideMain = true;
myFunction() {
var insideFunction = true;
nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
注意 nestedFunction() 可以訪問所有的變量, 包含頂級變量。
10、Lexical closures(詞法閉包)
一個 閉包 是一個方法對象,不管該對象在何處被調(diào)用, 該對象都可以訪問其作用域內(nèi) 的變量。
方法可以封閉定義到其作用域內(nèi)的變量。
下面的示例中,makeAdder() 捕獲到了變量 addBy。
不管你在那里執(zhí)行 makeAdder() 所返回的函數(shù),都可以使用 addBy 參數(shù)。
/// Returns a function that adds [addBy] to the
/// function's argument.
///num is a int or double pointer
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
main() {
// Create a function that adds 2.
var add2 = makeAdder(2);//num point to add2,equal 2
// Create a function that adds 4.
var add4 = makeAdder(4);//num point to add4,equal 4
assert(add2(3) == 5);
assert(add4(3) == 7);
}
11、Testing functions for equality(測試函數(shù)是否相等)
下面是測試頂級方法、靜態(tài)函數(shù)和實例函數(shù) 相等的示例:
foo() {} // A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
main() {
var x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
// Comparing static methods.
x = A.bar;
assert(A.bar == x);
// Comparing instance methods.
var v = new A(); // Instance #1 of A
var w = new A(); // Instance #2 of A
var y = w;
x = w.baz;
// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);
// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}
12、Return values(返回值)
所有的函數(shù)都返回一個值。如果沒有指定返回值,則 默認把語句 return null; 作為函數(shù)的最后一個語句執(zhí)行。
13、Operators(操作符)
下表是 Dart 中定義的操作符。 很多操作符都可以重載:
描述 操作符
unary postfix expr++ expr-- () [] . ?.
unary prefix -expr !expr ~expr ++expr --expr
multiplicative * / % ~/
additive + -
shift << >>
bitwise AND &
bitwise XOR ^
bitwise OR |
relational and type test
>= > <= < as is is!
equality == !=
logical AND &&
logical OR ||
if null ??
conditional expr1 ? expr2 : expr3
cascade ..
assignment = *= /= ~/= %= += -= <<= >>= &= ^= |= ??=
1)Type test operators(類型判定操作符)
as、 is、 和 is! 操作符是在運行時判定對象 類型的操作符:
| 操作符 | 解釋 |
|---|---|
| as | 類型轉(zhuǎn)換 |
| is | 如果對象是指定的類型返回 True |
| is! | 如果對象是指定的類型返回 False |
只有當(dāng) obj 實現(xiàn)了 T 的接口, obj is T 才是 true。例如 obj is Object 總是 true。
使用 as 操作符把對象轉(zhuǎn)換為特定的類型。 一般情況下,你可以把它當(dāng)做用 is 判定類型然后調(diào)用 所判定對象的函數(shù)的縮寫形式。
例如下面的 示例:
if (emp is Person) { // Type check
emp.firstName = 'Bob';
}
使用 as 操作符可以簡化上面的代碼:
(emp as Person).firstName = 'Bob'
注意: 上面這兩個代碼效果是有區(qū)別的。如果 emp 是 null 或者不是 Person 類型, 則第一個示例使用 is 則不會執(zhí)行條件里面的代碼,而第二個情況使用 as 則會拋出一個異常。
2)Assignment operators(賦值操作符)
使用 = 操作符來賦值。 但是還有一個 ??= 操作符用來指定 值為 null 的變量的值。
a = value; // 給 a 變量賦值
b ??= value; // 如果 b 是 null,則賦值給 b;
// 如果不是 null,則 b 的值保持不變
3)Conditional expressions(條件表達式)
Dart 有兩個特殊的操作符可以用來替代 if-else 語句:
- condition ? expr1 : expr2
如果 condition 是 true,執(zhí)行 expr1 (并返回執(zhí)行的結(jié)果); 否則執(zhí)行 expr2 并返回其結(jié)果。
- expr1 ?? expr2
如果 expr1 是 non-null,返回其值; 否則執(zhí)行 expr2 并返回其結(jié)果。
如果你需要基于布爾表達式 的值來賦值, 考慮使用 ?:。
var finalStatus = m.isFinal ? 'final' : 'not final'
如果布爾表達式是測試值是否為 null, 考慮使用 ??。
String toString() => msg ?? super.toString()
下面是一樣效果的實現(xiàn):
// Slightly longer version uses ?: operator.
String toString() => msg == null ? super.toString() : msg;
// Very long version uses if-else statement.
String toString() {
if (msg == null) {
return super.toString();
} else {
return msg;
}
}
4)Cascade notation (..)(級聯(lián)操作符)
級聯(lián)操作符 (..) 可以在同一個對象上 連續(xù)調(diào)用多個函數(shù)以及訪問成員變量。 使用級聯(lián)操作符可以避免創(chuàng)建 臨時變量, 并且寫出來的代碼看起來 更加流暢:
例如下面的代碼:
querySelector('#button') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
第一個方法 querySelector() 返回了一個 selector 對象。 后面的級聯(lián)操作符都是調(diào)用這個對象的成員, 并忽略每個操作 所返回的值。
上面的代碼和下面的代碼功能一樣:
var button = querySelector('#button');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
級聯(lián)調(diào)用也可以嵌套:
final addressBook = (new AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (new PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
在方法上使用級聯(lián)操作符需要非常小心, 例如下面的代碼就是不合法的:
// Does not work
var sb = new StringBuffer();
sb.write('foo')..write('bar');
sb.write() 函數(shù)返回一個 void, 無法在 void 上使用級聯(lián)操作符。
注意: 嚴格來說, 兩個點的級聯(lián)語法不是一個操作符。 只是一個 Dart 特殊語法。
5)Other operators(其他操作符)
下面是其他操作符:
Operator Name Meaning
- () 使用方法 代表調(diào)用一個方法
- [] 訪問 List 訪問 list 中特定位置的元素
- . 訪問 Member 訪問元素,例如foo.bar代表訪問foo的bar成員
- ?. 條件成員訪問 和.類似,但是左邊的操作對象不能為 null,例如foo?.bar如果foo為 null 則返回 null,否則返回bar成員
14、Control flow statements(流程控制語句)
可以使用下面的語句來控制 Dart 代碼的流程:
- if and else
- for loops
- while and do-while loops
- break and continue
- switch and case
- assert
- 使用 try-catch 和 throw 還能影響控制流程的 跳轉(zhuǎn)。
1)For loops
可以使用標(biāo)準的 for 循環(huán):
var message = new StringBuffer("Dart is fun");
for (var i = 0; i < 5; i++) {
message.write('!');
}
Dart for 循環(huán)中的閉包會捕獲循環(huán)的 index 索引值, 來避免 JavaScript 中常見的問題。例如:
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
如果要遍歷的對象實現(xiàn)了 Iterable 接口,則可以使用 forEach() 方法。如果沒必要當(dāng)前遍歷的索引,則使用 forEach() 方法 是個非常好的選擇:
candidates.forEach((candidate) => candidate.interview())
List 和 Set 等實現(xiàn)了 Iterable 接口的類還支持 for-in 形式的 遍歷:
var collection = [0, 1, 2];
for (var x in collection) {
print(x);
}
2)Break and continue
使用 break 來終止循環(huán):
while (true) {
if (shutDownRequested()) break;
processIncomingRequests();
}
使用 continue 來開始下一次循環(huán):
for (int i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.yearsExperience < 5) {
continue;
}
candidate.interview();
}
上面的代碼在實現(xiàn) Iterable 接口對象上可以使用下面的寫法:
candidates.where((c) => c.yearsExperience >= 5)//where apply condition to create new collection
.forEach((c) => c.interview());
3)Switch and case
Dart 中的 Switch 語句使用 == 比較 integer、string、或者編譯時常量。
比較的對象必須都是同一個類的實例(并且不是 其之類),class 必須沒有覆寫 == 操作符。
每個非空的 case 語句都必須有一個 break 語句。
另外還可以通過 continue、 throw 或 者 return 來結(jié)束非空 case 語句。
當(dāng)沒有 case 語句匹配的時候,可以使用 default 語句來匹配這種默認情況。
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 語句, 編譯的時候?qū)霈F(xiàn)一個錯誤:
var command = 'OPEN';
switch (command) {
case 'OPEN':
executeOpen();
// ERROR: Missing break causes an exception!!
case 'CLOSED':
executeClosed();
break;
}
但是,在 Dart 中的空 case 語句中可以不要 break 語句:
var command = 'CLOSED';
switch (command) {
case 'CLOSED': // Empty case falls through.
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
如果你需要實現(xiàn)這種繼續(xù)到下一個 case 語句中繼續(xù)執(zhí)行,則可以 使用 continue 語句跳轉(zhuǎn)到對應(yīng)的標(biāo)簽(label)處繼續(xù)執(zhí)行:
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// Continues executing at the nowClosed label.
nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
每個 case 語句可以有局部變量,局部變量 只有在這個語句內(nèi)可見。
15、Exceptions(異常)
代碼中可以出現(xiàn)異常和捕獲異常。異常表示一些 未知的錯誤情況。如果異常沒有捕獲, 則異常會拋出,導(dǎo)致 拋出異常的代碼終止執(zhí)行。
和 Java 不同的是,所有的 Dart 異常是非檢查異常。 方法不一定聲明了他們所拋出的異常, 并且你不要求捕獲任何異常。
Dart 提供了 Exception 和 Error 類型, 以及一些子類型。你還 可以定義自己的異常類型。但是, Dart 代碼可以 拋出任何非 null 對象為異常,不僅僅是實現(xiàn)了 Exception 或者 Error 的對象。
1)Throw
下面是拋出或者 扔出一個異常的示例:
throw new FormatException('Expected at least 1 section')
還可以拋出任意的對象:
throw 'Out of llamas!'
由于拋出異常是一個表達式,所以可以在 => 語句中使用,也可以在其他能使用表達式的地方拋出異常。
distanceTo(Point other) =>
throw new UnimplementedError();
2)Catch
捕獲異??梢员苊猱惓@^續(xù)傳遞(你重新拋出rethrow異常除外)。 捕獲異常給你一個處理 該異常的機會:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
對于可以拋出多種類型異常的代碼,你可以指定 多個捕獲語句。每個語句分別對應(yīng)一個異常類型, 如果捕獲語句沒有指定異常類型,則 該可以捕獲任何異常類型:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
如之前代碼所示,你可以使用on 或者 catch 來聲明捕獲語句,也可以 同時使用。使用 on 來指定異常類型,使用 catch 來 捕獲異常對象。
函數(shù) catch() 可以帶有一個或者兩個參數(shù), 第一個參數(shù)為拋出的異常對象, 第二個為堆棧信息 (一個 StackTrace 對象)。
{
...
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
使用 rethrow 關(guān)鍵字可以 把捕獲的異常給 重新拋出。
final foo = '';
void misbehave() {
try {
foo = "You can't change a final variable's value.";
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
16、Classes
Dart 是一個面向?qū)ο缶幊陶Z言,同時支持基于 mixin 的繼承機制。
每個對象都是一個類的實例,所有的類都繼承于 Object。
基于 Mixin 的繼承 意味著每個類(Object 除外) 都只有一個超類,一個類的代碼可以在其他 多個類繼承中重復(fù)使用。
1)構(gòu)造函數(shù)
由于把構(gòu)造函數(shù)參數(shù)賦值給實例變量的場景太常見了, Dart 提供了一個語法糖來簡化這個操作:
class Point {
num x;
num y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
2)Constructors aren’t inherited(構(gòu)造函數(shù)不會繼承)
子類不會繼承超類的構(gòu)造函數(shù)。 子類如果沒有定義構(gòu)造函數(shù),則只有一個默認構(gòu)造函數(shù) (沒有名字沒有參數(shù))。
3)Named constructors(命名構(gòu)造函數(shù))
使用命名構(gòu)造函數(shù)可以為一個類實現(xiàn)多個構(gòu)造函數(shù), 或者使用命名構(gòu)造函數(shù)來更清晰的表明你的意圖:
class Point {
num x;
num y;
Point(this.x, this.y);
// Named constructor
Point.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
注意:構(gòu)造函數(shù)不能繼承,所以超類的命名構(gòu)造函數(shù) 也不會被繼承。如果你希望 子類也有超類一樣的命名構(gòu)造函數(shù), 你必須在子類中自己實現(xiàn)該構(gòu)造函數(shù)。
4)Invoking a non-default superclass
constructor(調(diào)用超類構(gòu)造函數(shù))
默認情況下,子類的構(gòu)造函數(shù)會自動調(diào)用超類的 無名無參數(shù)的默認構(gòu)造函數(shù)。
超類的構(gòu)造函數(shù)在子類構(gòu)造函數(shù)體開始執(zhí)行的位置調(diào)用。 如果提供了一個 initializer list(初始化參數(shù)列表) ,則初始化參數(shù)列表在超類構(gòu)造函數(shù)執(zhí)行之前執(zhí)行。
下面是構(gòu)造函數(shù)執(zhí)行順序:
initializer list(初始化參數(shù)列表)
super class’s no-arg constructor(超類的無名構(gòu)造函數(shù))
main class’s no-arg constructor(主類的無名構(gòu)造函數(shù))
如果超類沒有無名無參數(shù)構(gòu)造函數(shù), 則你需要手工的調(diào)用超類的其他構(gòu)造函數(shù)。
在構(gòu)造函數(shù)參數(shù)后使用冒號 (:) 可以調(diào)用 超類構(gòu)造函數(shù)。
下面的示例中,Employee 類的構(gòu)造函數(shù)調(diào)用 了超類 Person 的命名構(gòu)造函數(shù)。
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// Person does not have a default constructor;
// you must call 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';
}
由于超類構(gòu)造函數(shù)的參數(shù)在構(gòu)造函數(shù)執(zhí)行之前執(zhí)行,所以 參數(shù)可以是一個表達式或者 一個方法調(diào)用:
class Employee extends Person {
// ...
Employee() : super.fromJson(findDefaultData());
}
注意: 如果在構(gòu)造函數(shù)的初始化列表中使用 super(),需要把它放到最后。
警告: 調(diào)用超類構(gòu)造函數(shù)的參數(shù)無法訪問 this。 例如,參數(shù)可以為靜態(tài)函數(shù)但是不能是實例函數(shù)。
5)Initializer list(初始化列表)
在構(gòu)造函數(shù)體執(zhí)行之前除了可以調(diào)用超類構(gòu)造函數(shù)之外,還可以 初始化實例參數(shù)。 使用逗號分隔初始化表達式。
class Point {
num x;
num y;
Point(this.x, this.y);
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map jsonMap)
: x = jsonMap['x'],
y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
警告: 初始化表達式等號右邊的部分不能訪問 this。
初始化列表非常適合用來設(shè)置 final 變量的值。 下面示例代碼中初始化列表設(shè)置了三個 final 變量的值。
import 'dart:math';
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
main() {
var p = new Point(2, 3);
print(p.distanceFromOrigin);
}
6)Redirecting constructors(重定向構(gòu)造函數(shù))
重定向構(gòu)造函數(shù)的時候一個構(gòu)造函數(shù)會調(diào)動類中的其他構(gòu)造函數(shù)。 一個重定向構(gòu)造函數(shù)是沒有代碼的,在構(gòu)造函數(shù)聲明后,使用 冒號調(diào)用其他構(gòu)造函數(shù)。
class Point {
num x;
num y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
7)Constant constructors(常量構(gòu)造函數(shù))
如果你的類提供一個狀態(tài)不變的對象,你可以把這些對象 定義為編譯時常量。要實現(xiàn)這個功能,需要定義一個 const 構(gòu)造函數(shù), 并且聲明所有類的變量為 final。
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
}
8)Factory constructors(工廠方法構(gòu)造函數(shù))
如果一個構(gòu)造函數(shù)并不總是返回一個新的對象,則使用 factory 來定義 這個構(gòu)造函數(shù)。例如,一個工廠構(gòu)造函數(shù) 可能從緩存中獲取一個實例并返回,或者 返回一個子類型的實例。
下面代碼演示工廠構(gòu)造函數(shù) 如何從緩存中返回對象。
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to the _ in front
// of its name.
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);
}
}
}
注意: 工廠構(gòu)造函數(shù)無法訪問 this。
使用 new 關(guān)鍵字來調(diào)用工廠構(gòu)造函數(shù)。
var logger = new Logger('UI');
logger.log('Button clicked');
17、Methods(函數(shù))
函數(shù)是類中定義的方法,是類對象的行為。
1)Instance methods(實例函數(shù))
對象的實例函數(shù)可以訪問 this。 例如下面示例中的 distanceTo() 函數(shù) 就是實例函數(shù):
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);
}
}
2)Getters and setters
Getters 和 setters 是用來設(shè)置和訪問對象屬性的特殊 函數(shù)。每個實例變量都隱含的具有一個 getter, 如果變量不是 final 的則還有一個 setter。 你可以通過實行 getter 和 setter 來創(chuàng)建新的屬性, 使用 get 和 set 關(guān)鍵字定義 getter 和 setter:
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: 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 的好處是,你可以開始使用實例變量,后來 你可以把實例變量用函數(shù)包裹起來,而調(diào)用你代碼的地方不需要修改。
3)Abstract methods(抽象函數(shù))
實例函數(shù)、 getter、和 setter 函數(shù)可以為抽象函數(shù), 抽象函數(shù)是只定義函數(shù)接口但是沒有實現(xiàn)的函數(shù),由子類來 實現(xiàn)該函數(shù)。如果用分號來替代函數(shù)體則這個函數(shù)就是抽象函數(shù)。
abstract class Doer {
// ...Define instance variables and methods...
void doSomething(); // Define an abstract method.
}
class EffectiveDoer extends Doer {
void doSomething() {
// ...Provide an implementation, so the method is not abstract here...
}
}
調(diào)用一個沒實現(xiàn)的抽象函數(shù)會導(dǎo)致運行時異常。
4)Overridable operators(可覆寫的操作符)
下表中的操作符可以被覆寫。 例如,如果你定義了一個 Vector 類, 你可以定義一個 + 函數(shù)來實現(xiàn)兩個向量相加。
< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
– % >>
下面是覆寫了 + 和 - 操作符的示例:
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
/// Overrides + (a + b).
Vector operator +(Vector v) {
return new Vector(x + v.x, y + v.y);
}
/// Overrides - (a - b).
Vector operator -(Vector v) {
return new Vector(x - v.x, y - v.y);
}
}
main() {
final v = new Vector(2, 3);
final w = new Vector(2, 2);
// v == (2, 3)
assert(v.x == 2 && v.y == 3);
// v + w == (4, 5)
assert((v + w).x == 4 && (v + w).y == 5);
// v - w == (0, 1)
assert((v - w).x == 0 && (v - w).y == 1);
}
如果你覆寫了 == ,則還應(yīng)該覆寫對象的 hashCode getter 函數(shù)。
18、Abstract classes(抽象類)
使用 abstract 修飾符定義一個 抽象類—一個不能被實例化的類。 抽象類通常用來定義接口, 以及部分實現(xiàn)。如果你希望你的抽象類 是可示例化的,則定義一個 工廠 構(gòu)造函數(shù)。
抽象類通常具有 抽象函數(shù)。 下面是定義具有抽象函數(shù)的 抽象類的示例:
// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
// ...Define constructors, fields, methods...
void updateChildren(); // Abstract method.
}
下面的類不是抽象的,但是繼承了一個抽象類,這樣 的類是可以被實例化的:
class SpecializedContainer extends AbstractContainer {
// ...Define more constructors, fields, methods...
void updateChildren() {
// ...Implement updateChildren()...
}
// Abstract method causes a warning but
// doesn't prevent instantiation.
void doSomething();
}
19、Implicit interfaces(隱式接口)
每個類都隱式的定義了一個包含所有實例成員的接口, 并且這個類實現(xiàn)了這個接口。如果你想 創(chuàng)建類 A 來支持 類 B 的 api,而不想繼承 B 的實現(xiàn), 則類 A 應(yīng)該實現(xiàn) B 的接口。
一個類可以通過 implements 關(guān)鍵字來實現(xiàn)一個或者多個接口, 并實現(xiàn)每個接口定義的 API。 例如:
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final _name;
// Not in the interface, since this is a constructor.
Person(this._name);
// In the interface.
String greet(who) => 'Hello, $who. I am $_name.';
}
// An implementation of the Person interface.
class Imposter implements Person {
// We have to define this, but we don't use it.
final _name = "";
String greet(who) => 'Hi $who. Do you know who I am?';
}
greetBob(Person person) => person.greet('bob');
main() {
print(greetBob(new Person('kathy')));
print(greetBob(new Imposter()));
}
下面是實現(xiàn)多個接口 的示例:
class Point implements Comparable, Location {
// ...
}
20、Extending a class(擴展類)
使用 extends 定義子類, supper 引用 超類:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ...
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ...
}
子類可以覆寫實例函數(shù),getter 和 setter。 下面是覆寫 Object 類的 noSuchMethod() 函數(shù)的例子, 如果調(diào)用了對象上不存在的函數(shù),則就會觸發(fā) noSuchMethod() 函 數(shù)。
class A {
// Unless you override noSuchMethod, using a
// non-existent member results in a NoSuchMethodError.
void noSuchMethod(Invocation mirror) {
print('You tried to use a non-existent member:' +
'${mirror.memberName}');
}
}
還可以使用 @override 注解來 表明你的函數(shù)是想覆寫超類的一個函數(shù):
class A {
@override
void noSuchMethod(Invocation mirror) {
// ...
}
}
如果你使用 noSuchMethod() 函數(shù)來實現(xiàn)每個可能的 getter 、setter、 以及其他類型的函數(shù),你可以使用 @proxy 注解來避免警告信息:
@proxy
class A {
void noSuchMethod(Invocation mirror) {
// ...
}
}
如果你知道編譯時的具體類型,則可以 實現(xiàn)這些類來避免警告,和 使用 @proxy 效果一樣:
class A implements SomeClass, SomeOtherClass {
void noSuchMethod(Invocation mirror) {
// ...
}
}
21、Enumerated types(枚舉類型)
枚舉類型通常稱之為 enumerations 或者 enums, 是一種特殊的類,用來表現(xiàn)一個固定 數(shù)目的常量。
1)Using enums
使用 enum 關(guān)鍵字來定義枚舉類型:
enum Color {
red,
green,
blue
}
枚舉類型中的每個值都有一個 index getter 函數(shù), 該函數(shù)返回該值在枚舉類型定義中的位置(從 0 開始)。 例如,第一個枚舉值的位置為 0, 第二個為 1.
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
枚舉的 values 常量可以返回 所有的枚舉值。
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
以在 switch 語句 中使用枚舉。 如果在 switch (e) 中的 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'
}
枚舉類型具有如下的限制:
無法繼承枚舉類型、無法使用 mix in、無法實現(xiàn)一個枚舉類型
無法顯示的初始化一個枚舉類型
22、Adding features to a class:
mixins(為類添加新的功能)
Mixins 是一種在多類繼承中重用 一個類代碼的方法。
使用 with 關(guān)鍵字后面為一個或者多個 mixin 名字來使用 mixin。 下面是示例顯示了如何使用 mixin:
class Musician extends Performer with Musical {
// ...
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
定義一個類繼承 Object,該類沒有構(gòu)造函數(shù), 不能調(diào)用 super ,則該類就是一個 mixin。例如:
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');
}
}
}
23、Class variables and methods(類變量和函數(shù))
使用 static 關(guān)鍵字來實現(xiàn)類級別的變量和函數(shù)。
1)Static variables(靜態(tài)變量)
靜態(tài)變量對于類級別的狀態(tài)是 非常有用的:
class Color {
static const red =
const Color('red'); // A constant static variable.
final String name; // An instance variable.
const Color(this.name); // A constant constructor.
}
main() {
assert(Color.red.name == 'red');
}
靜態(tài)變量在第一次使用的時候才被初始化。
注意: 這里準守代碼風(fēng)格推薦 命名規(guī)則,使用 lowerCamelCase 來命名常量。
2)Static methods(靜態(tài)函數(shù))
靜態(tài)函數(shù)不再類實例上執(zhí)行, 所以無法訪問 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);
}
注意: 對于通用的或者經(jīng)常使用的靜態(tài)函數(shù),考慮 使用頂級方法而不是靜態(tài)函數(shù)。
靜態(tài)函數(shù)還可以當(dāng)做編譯時常量使用。例如, 你可以把靜態(tài)函數(shù)當(dāng)做常量構(gòu)造函數(shù)的參數(shù)來使用。
24、Generics(泛型)
1)Using collection literals(使用集合字面量)
List 和 map 字面量也是可以參數(shù)化的。 參數(shù)化定義 list 需要在中括號之前 添加 <type> , 定義 map 需要在大括號之前 添加 <keyType,valueType>。
如果你需要更加安全的類型檢查,則可以使用 參數(shù)化定義。下面是一些示例:
var names = <String>['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
2)Using parameterized types withconstructors(在構(gòu)造函數(shù)中使用泛型)
在調(diào)用構(gòu)造函數(shù)的時候, 在類名字后面使用尖括號(<...>)來指定 泛型類型。例如:
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = new Set<String>.from(names);
下面代碼創(chuàng)建了一個 key 為 integer, value 為 View 類型 的 map:
var views = new Map<int, View>();
3)Generic collections and the types they contain
Dart 的泛型類型是固化的,在運行時有也 可以判斷具體的類型。例如在運行時(甚至是成產(chǎn)模式) 也可以檢測集合里面的對象類型:
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
注意 is 表達式只是判斷集合的類型,而不是集合里面具體對象的類型。 在生產(chǎn)模式,List<String> 變量可以包含 非字符串類型對象。對于這種情況, 你可以選擇分別判斷每個對象的類型或者 處理類型轉(zhuǎn)換異常 (參考 Exceptions)。
4)Restricting the parameterized type(限制泛型類型)
當(dāng)使用泛型類型的時候,你 可能想限制泛型的具體類型。 使用 extends 可以實現(xiàn)這個功能:
// T must be SomeBaseClass or one of its descendants.
class Foo<T extends SomeBaseClass> {...}
class Extender extends SomeBaseClass {...}
void main() {
// It's OK to use SomeBaseClass or any of its subclasses inside <>.
var someBaseClassFoo = new Foo<SomeBaseClass>();
var extenderFoo = new Foo<Extender>();
// It's also OK to use no <> at all.
var foo = new Foo();
// Specifying any non-SomeBaseClass type results in a warning and, in
// checked mode, a runtime error.
// var objectFoo = new Foo<Object>();
}
5)Using generic methods(使用泛型函數(shù))
一開始,泛型只能在 Dart 類中使用。 新的語法也支持在函數(shù)和方法上使用泛型了。
T first<T>(List<T> ts) {
// ...Do some initial work or error checking, then...
T tmp ?= ts[0];
// ...Do some additional checking or processing...
return tmp;
}
這里的 first (<T>) 泛型可以在如下地方使用 參數(shù) T :
函數(shù)的返回值類型 (T).
參數(shù)的類型 (List<T>).
局部變量的類型 (T tmp).
25、Libraries and visibility(庫和可見性)
使用 import 和 library 指令可以幫助你創(chuàng)建 模塊化的可分享的代碼。庫不僅僅提供 API,
還是一個私有單元:以下劃線 (_) 開頭的標(biāo)識符只有在庫 內(nèi)部可見。每個 Dart app 都是一個庫, 即使沒有使用 library 命令也是一個庫。
庫可以使用 Dart package 工具部署。
1)Using libraries(使用庫)
使用 import 來指定一個庫如何使用另外 一個庫。
例如, Dart web 應(yīng)用通常使用 dart:html 庫,然后可以這樣導(dǎo)入庫:
import 'dart:html';
import 必須參數(shù)為庫 的 URI。
對于內(nèi)置的庫,URI 使用特殊的 dart: scheme。 對于其他的庫,你可以使用文件系統(tǒng)路徑或者 package:scheme。 package: scheme 指定的庫通過包管理器來提供, 例如 pub 工具。
import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';
2)Specifying a library prefix(指定庫前綴)
如果你導(dǎo)入的兩個庫具有沖突的標(biāo)識符, 則你可以使用庫的前綴來區(qū)分。 例如,如果 library1 和 library2 都有一個名字為 Element 的類, 你可以這樣使用:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element(); // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
3)Importing only part of a library(導(dǎo)入庫的一部分)
如果你只使用庫的一部分功能,則可以選擇需要導(dǎo)入的 內(nèi)容。例如:
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
4)Lazily loading a library(延遲載入庫)
Deferred loading (也稱之為 lazy loading) 可以讓應(yīng)用在需要的時候再 加載庫。 下面是一些使用延遲加載庫的場景:
- 減少 APP 的啟動時間。
執(zhí)行 A/B 測試,例如 嘗試各種算法的 不同實現(xiàn)。
加載很少使用的功能,例如可選的屏幕和對話框。
要延遲加載一個庫,需要先使用 deferred as 來 導(dǎo)入:
import 'package:deferred/hello.dart' deferred as hello;
當(dāng)需要使用的時候,使用庫標(biāo)識符調(diào)用 loadLibrary() 函數(shù)來加載庫:
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
在前面的代碼, 使用 await 關(guān)鍵字暫停代碼執(zhí)行一直到庫加載完成。
在一個庫上你可以多次調(diào)用 loadLibrary() 函數(shù)。 但是該庫只是載入一次。
使用延遲加載庫的時候,請注意一下問題:
延遲加載庫的常量在導(dǎo)入的時候是不可用的。 只有當(dāng)庫加載完畢的時候,庫中常量才可以使用。
在導(dǎo)入文件的時候無法使用延遲庫中的類型。 如果你需要使用類型,則考慮把接口類型移動到另外一個庫中, 讓兩個庫都分別導(dǎo)入這個接口庫。
Dart 隱含的把 loadLibrary() 函數(shù)導(dǎo)入到使用 deferred as 的命名空間 中。 loadLibrary() 方法返回一個 Future。
26、Asynchrony support(異步支持)
Dart 有一些語言特性來支持 異步編程。 最常見的特性是 async 方法和 await 表達式。
Dart 庫中有很多返回 Future 或者 Stream 對象的方法。 這些方法是 異步的: 這些函數(shù)在設(shè)置完基本的操作 后就返回了, 而無需等待操作執(zhí)行完成。
例如讀取一個文件,在打開文件后就返回了。
有兩種方式可以使用 Future 對象中的 數(shù)據(jù):
- 使用 async 和 await
- 使用 Future API
同樣,從 Stream 中獲取數(shù)據(jù)也有兩種 方式:
- 使用 async 和一個 異步 for 循環(huán) (await for)
- 使用 Stream API
使用 async 和 await 的代碼是異步的, 但是看起來有點像同步代碼。
例如,下面是一些使用 await 來 等待異步方法返回的示例:
await lookUpVersion()
要使用 await,其方法必須帶有 async 關(guān)鍵字:
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...
}
1)Declaring async functions(聲明異步方法)
一個 async 方法 是函數(shù)體被標(biāo)記為 async 的方法。 雖然異步方法的執(zhí)行可能需要一定時間,但是 異步方法立刻返回 - 在方法體還沒執(zhí)行之前就返回了。
checkVersion() async {
// ...
}
lookUpVersion() async => /* ... */;
在一個方法上添加 async 關(guān)鍵字,則這個方法返回值為 Future。 例如,下面是一個返回字符串 的同步方法:
String lookUpVersionSync() => '1.0.0';
如果使用 async 關(guān)鍵字,則該方法 返回一個 Future,并且 認為該函數(shù)是一個耗時的操作。
Future<String> lookUpVersion() async => '1.0.0';
注意,方法的函數(shù)體并不需要使用 Future API。 Dart 會自動在需要的時候創(chuàng)建 Future 對象。
2)Using await expressions with Futures(使用 await 表達式)
await 表達式具有如下的形式:
await expression
在一個異步方法內(nèi)可以使用多次 await 表達式。 例如,下面的示例使用了三次 await 表達式 來執(zhí)行相關(guān)的功能:
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
在 await expression 中, expression 的返回值通常是一個 Future; 如果返回的值不是 Future,則 Dart 會自動把該值放到 Future 中返回。
Future 對象代表返回一個對象的承諾(promise)。 await expression 執(zhí)行的結(jié)果為這個返回的對象。 await expression 會阻塞住,直到需要的對象返回為止。
如果 await 無法正常使用,請確保是在一個 async 方法中。 例如要在 main() 方法中使用 await, 則 main() 方法的函數(shù)體必須標(biāo)記為 async:
main() async {
checkVersion();
print('In main: version is ${await lookUpVersion()}');
}
3)Using asynchronous for loops with Streams(在循環(huán)中使用異步)
異步 for 循環(huán)具有如下的形式:
await for (variable declaration in expression) {
// Executes each time the stream emits a value.
}
上面 expression 返回的值必須是 Stream 類型的。 執(zhí)行流程如下:
1.等待直到 stream 返回一個數(shù)據(jù)
2.使用 stream 返回的參數(shù) 執(zhí)行 for 循環(huán)代碼,
3.重復(fù)執(zhí)行 1 和 2 直到 stream 數(shù)據(jù)返回完畢。
4.使用 break 或者 return 語句可以 停止接收 stream 的數(shù)據(jù), 這樣就跳出了 for 循環(huán)并且 從 stream 上取消注冊了。
如果異步 for 循環(huán)不能正常工作, 確保是在一個 async 方法中使用。 例如,要想在 main() 方法中使用異步 for 循環(huán),則需要把 main() 方法的函數(shù)體標(biāo)記為 async:
main() async {
...
await for (var request in requestServer) {
handleRequest(request);
}
...
}
27、Callable classes(可調(diào)用的類)
如果 Dart 類實現(xiàn)了 call() 函數(shù)則 可以當(dāng)做方法來調(diào)用。
在下面的示例中,WannabeFunction 類定義了一個 call() 方法,該方法有三個字符串參數(shù),并且返回三個字符串 串聯(lián)起來的結(jié)果。
class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannabeFunction();
var out = wf("Hi","there,","gang");
print('$out');
}
28、Isolates
現(xiàn)代的瀏覽器以及移動瀏覽器都運行在多核 CPU 系統(tǒng)上。 要充分利用這些 CPU,開發(fā)者一般使用共享內(nèi)存 數(shù)據(jù)來保證多線程的正確執(zhí)行。
然而, 多線程共享數(shù)據(jù)通常會導(dǎo)致很多潛在的問題,并導(dǎo)致代碼運行出錯。
所有的 Dart 代碼在 isolates 中運行而不是線程。 每個 isolate 都有自己的堆內(nèi)存,并且確保每個 isolate 的狀態(tài)都不能被其他 isolate 訪問。
29、Typedefs
在 Dart 語言中,方法也是對象。 使用 typedef, 或者 function-type alias 來為方法類型命名, 然后可以使用命名的方法。
當(dāng)把方法類型賦值給一個變量的時候,typedef 保留類型信息。
下面的代碼沒有使用 typedef:
class SortedCollection {
Function compare;
SortedCollection(int f(Object a, Object b)) {
compare = f;
}
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
main() {
SortedCollection coll = new SortedCollection(sort);
// 我們只知道 compare 是一個 Function 類型,
// 但是不知道具體是何種 Function 類型?
assert(coll.compare is Function);
}
當(dāng)把 f 賦值給 compare 的時候, 類型信息丟失了。 f 的類型是 (Object, Object) → int (這里 → 代表返回值類型), 當(dāng)然該類型是一個 Function。
如果我們使用顯式的名字并保留類型信息, 開發(fā)者和工具可以使用 這些信息:
typedef int Compare(Object a, Object b);
class SortedCollection {
Compare compare;
SortedCollection(this.compare);
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
main() {
SortedCollection coll = new SortedCollection(sort);
assert(coll.compare is Function);
assert(coll.compare is Compare);
}
由于 typedefs 只是別名,他們還提供了一種 判斷任意 function 的類型的方法。例如:
typedef int Compare(int a, int b);
int sort(int a, int b) => a - b;
main() {
assert(sort is Compare); // True!
}
30、Metadata(元數(shù)據(jù))
使用元數(shù)據(jù)給你的代碼添加其他額外信息。 元數(shù)據(jù)注解是以 @ 字符開頭,后面是一個編譯時 常量(例如 deprecated)或者 調(diào)用一個常量構(gòu)造函數(shù)。
有三個注解所有的 Dart 代碼都可以使用: @deprecated、 @override、 和 @proxy。關(guān)于 @override 和 @proxy 示例請參考 Extending a class。 下面是使用 @deprecated 的 示例:
class Television {
/// _Deprecated: Use [turnOn] instead._
@deprecated
void activate() {
turnOn();
}
/// Turns the TV's power on.
void turnOn() {
print('on!');
}
}
你還可以定義自己的元數(shù)據(jù)注解。 下面的示例定義了一個帶有兩個參數(shù)的 @todo 注解:
library todo;
class todo {
final String who;
final String what;
const todo(this.who, this.what);
}
使用 @todo 注解的示例:
import 'todo.dart';
@todo('seth', 'make this do something')
void doSomething() {
print('do something');
}
元數(shù)據(jù)可以在 library、 class、 typedef、 type parameter、 constructor、 factory、 function、 field、 parameter、或者 variable 聲明之前使用,也可以在 import 或者 export 指令之前使用。 使用反射可以在運行時獲取元數(shù)據(jù) 信息。
31、Comments(注釋)
1)Documentation comments
文檔注釋可以使用 /// 開始, 也可以使用 /** 開始 并以 */ 結(jié)束。
在文檔注釋內(nèi), Dart 編譯器忽略除了中括號以外的內(nèi)容。 使用中括號可以引用 classes、 methods、 fields、 top-level variables、 functions、 和 parameters。中括號里面的名字使用 當(dāng)前注釋出現(xiàn)地方的語法范圍查找對應(yīng)的成員。
下面是一個引用其他類和成員 的文檔注釋:
/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
class Llama {
String name;
/// Feeds your llama [Food].
///
/// The typical llama eats one bale of hay per week.
void feed(Food food) {
// ...
}
/// Exercises your llama with an [activity] for
/// [timeLimit] minutes.
void exercise(Activity activity, int timeLimit) {
// ...
}
}
在生成的文檔中,[Food] 變?yōu)橐粋€連接 到 Food 類 API 文檔的鏈接。
使用 SDK 中的 文檔生成工具可以解析文檔并生成 HTML 網(wǎng)頁。
微信公眾號二維碼:
