@[toc]
Dart是谷歌開(kāi)發(fā)并在2011年亮相,2015推出了基于Dart語(yǔ)言的移動(dòng)應(yīng)用程序的開(kāi)發(fā)框架 Sky,后更名為 Flutter。經(jīng)過(guò)多年的發(fā)展和完善,F(xiàn)lutter 逐漸成為公司開(kāi)發(fā)應(yīng)用程序的新寵兒。
Dart是面向?qū)ο蟮摹㈩惗x的、單繼承的語(yǔ)言。它的語(yǔ)法類似C語(yǔ)言,可以轉(zhuǎn)譯為JavaScript,支持接口(interfaces)、混入(mixins)、抽象類(abstract classes)、具體化泛型(reified generics)、可選類型(optional typing)和sound type system。
體驗(yàn) Dart 程序
// 定義一個(gè)函數(shù)
printInteger(int aNumber) {
print('The number is $aNumber.'); // 打印輸出到控制臺(tái)。
}
// Dart 程序從 main() 函數(shù)開(kāi)始執(zhí)行。
main() {
var number = 42; // 聲明并初始化一個(gè)變量。
printInteger(number); // 調(diào)用一個(gè)函數(shù)。
}
Dart 應(yīng)用程序總是會(huì)從頂級(jí)函數(shù) main() 開(kāi)始執(zhí)行;
Dart 語(yǔ)言需要 ;
符號(hào)作為每一條語(yǔ)句的結(jié)束符號(hào);
$variableName
(或 ${expression}
) 表示字符串插值。
簡(jiǎn)介
Dart 在語(yǔ)法上和一些熱門的語(yǔ)言非常相似,例如C、JavaScript、Python、Swift。語(yǔ)法的設(shè)計(jì)上有著非常多的相似之處,如果你使用或者了解多門語(yǔ)言,你能很輕松的掌握 Dart 語(yǔ)言。
學(xué)習(xí)不同的編程語(yǔ)言,通過(guò)相互之間的類比,是了解和掌握語(yǔ)言之間的差異性必要條件,筆者這里只記錄一些值得玩味的點(diǎn),讓你能快速的一名 Dart 語(yǔ)言的開(kāi)發(fā)者。想要更深入的了解 Dart 語(yǔ)言特性,請(qǐng)查閱 官方開(kāi)發(fā)文檔 以及相關(guān)的社區(qū)。
變量
String name = 'Bob'; // 指定類型,不可接收其他類型
var name = 'Bob'; // 推斷出類型,不可接收其他類型
Object name = 'Bob'; // 可接收其他類型,只能使用Object中定義的方法和變量,類似NSObject
dynamic name = 'Bob'; // 可接收其他類型,可以使用所有可能的方法和變量,類似id
final 和 const
如果你從未打算更改一個(gè)變量,那么使用 final
或 const
,而不是 var
或者某一指定類型。 final
和 cons
修飾的變量只能被設(shè)置一次,區(qū)別在于, final
變量在第一次使用時(shí)被初始化,const
變量是一個(gè)編譯時(shí)期的常量。
//可以省略String這個(gè)類型聲明
final str = "hi world";
//final String str = "hi world";
const str1 = "hi world";
//const String str1 = "hi world";
內(nèi)置類型
Dart 語(yǔ)言內(nèi)置如下的類型:numbers、strings、booleans、list(數(shù)組)、sets(集合)maps(字典)、runes(Unicode字符)、symbols。
Dart 所有變量引用的都是對(duì)象類型,每個(gè)對(duì)象都是一個(gè)類型的實(shí)例。數(shù)字、函數(shù)或者 null 都是對(duì)象。所有的類都繼承自 Object 類。
-
numbers
Dart 有兩種類型的 number:int 和 double,兩個(gè)都是 num 的子類。
var x = 1; var hex = 0xDEADBEEF; var y = 1.1; double z = 1; // double z = 1.0.
Dart 盡管是強(qiáng)類型語(yǔ)言,但是在聲明變量的時(shí)候,類型是可選的,因此你可以指定變量類型,如 double,也可以使用 var 聲明,由 Dart 自動(dòng)進(jìn)行類型推斷。
-
Strings
var s1 = '使用單引號(hào)創(chuàng)建字符串字面量。'; var s2 = "雙引號(hào)也可以用于創(chuàng)建字符串字面量。"; var s3 = '使用單引號(hào)創(chuàng)建字符串時(shí)可以使用斜杠來(lái)轉(zhuǎn)義那些與單引號(hào)沖突的字符串:\'。'; var s4 = "而在雙引號(hào)中則不需要使用轉(zhuǎn)義與單引號(hào)沖突的字符串:'"; var s5 = r'在 raw 字符串中,轉(zhuǎn)義字符串 \n 會(huì)直接輸出 “\n” 而不是轉(zhuǎn)義為換行。'; var s6 = '可以拼接' '字符串' "即便它們不在同一行。"; var s7 = '使用加號(hào)+運(yùn)算符' + '也可以達(dá)到相同的效果。'; var s8 = """ 你可以像這樣創(chuàng)建多行字符串 """; var s9 = '字符串插值'; print ('Dart 有$s9,使用起來(lái)非常方便'); print ('使用${s9.substring(3,5)}表達(dá)式也非常方便');
-
Booleans
布爾類型只有兩個(gè)對(duì)象 true 和 false,兩者都是編譯時(shí)常量。Dart 需要顯示地檢查布爾值,不允許使用其他類型作為條件,例如 int 類型。
var fullName = ''; assert(fullName.isEmpty); bool hited = false; assert(hited);
-
Lists
Dart 中的數(shù)組類型的字面量和 JavaScript 中的數(shù)組字面量一樣。
var list1 = [1,2,3]; var count = list1.length; // 3 // 替換 list1[0] = 0; var list2 = [4,5,6]; // 拼接 var list3 = list1 + list2; // [0, 2, 3, 4, 5, 6] // 擴(kuò)展符號(hào) ... 和 ...? var list4 = [1, ...list2]; // [1, 4, 5, 6] var list5; // null var list6 = [1,...?list5]; // [1],數(shù)組可能為空,使用 ...? 避免異常 // 2.3版本的 Collection If bool promoActive = true; var list7 = [ 'Home', 'Furniture', 'Plants', if (promoActive) 'OutLet' ]; // 2.3版本的 Collection For var listOfInts = [1,2,3]; var list8 = [ '0', for (var i in listOfInts) '$i' ]; var list9 = [ for (var i =1; i<4; i++) i, for (var i in [4,5,6]) i ];
-
Sets
一組特定元素的無(wú)序集合。
// Dart 2.2 版本之后才加入 Set 字面量的創(chuàng)建方式 var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'}; // 創(chuàng)建空 set 類型,添加非指定類型時(shí)異常,不指定類型的{}是空Map類型 var names = <String>{}; // 添加一個(gè)或一組 names.add('luo'); names.addAll(['liang','guo']);
從 Dart2.3版本開(kāi)始,Set 和 List 一樣支持使用擴(kuò)展操作符 ... 和 ...? 以及 Collection If 和 Collection For 的方式創(chuàng)建實(shí)例。
-
Maps
Maps是一組 key 和 value 的集合對(duì)象,在 OC 中,我們稱之為字典類型。Dart 中的 key 和 value 可以是任何類型的對(duì)象。
// 字面量 var fruits = { "first":"Apple", "second":"pear" }; print(fruits); // 構(gòu)造函數(shù) var nobleGases = Map(); nobleGases['1'] = 'helium'; nobleGases['2'] = 'neon'; // 所有的key var keys = nobleGases.keys; // 所有的值 var values = nobleGases.values;
從 Dart2.3版本開(kāi)始,Map 和 List 一樣支持使用擴(kuò)展操作符 ... 和 ...? 以及 Collection If 和 Collection For 的方式創(chuàng)建實(shí)例。
-
Runes
Unicode 編碼為每一個(gè)字母、數(shù)字和符號(hào)都定義了一個(gè)唯一的數(shù)值。因?yàn)?Dart 中的字符串是一個(gè) UTF-16 的字符序列,所以如果想要表示 32 位的 Unicode 數(shù)值則需要一種特殊的語(yǔ)法。
通常使用 \uXXXX 來(lái)表示 Unicode 字符, XXXX 是一個(gè)四位數(shù)的 16 進(jìn)制數(shù)字。例如心形字符(?)的 Unicode 為 \u2665。對(duì)于不是四位數(shù)的 16 進(jìn)制數(shù)字,需要使用大括號(hào)將其括起來(lái)。例如大笑的 emoji 表情(??)的 Unicode 為 \u{1f600}。
import 'package:characters/characters.dart'; ... var hi = 'Hi ????'; print(hi); print('The last character: ${hi.characters.last}\n');
函數(shù)
Dart 中函數(shù)也是一種對(duì)象類型:Function,這就意味著函數(shù)可以作為變量、參數(shù)和返回值使用。
// 返回類型可選,可不寫
返回值類型 函數(shù)名(參數(shù)列表) {
函數(shù)體;
};
例如:
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != nil;
}
當(dāng)函數(shù)體只包含一個(gè)表達(dá)式,可以簡(jiǎn)寫成下面這樣,你可以經(jīng)常看到這種寫法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != nil;
可選參數(shù)
函數(shù)的參數(shù)有兩種:必須要參數(shù)和可選參數(shù)。必要參數(shù)定義在最前面,可選參數(shù)接在后面,可選參數(shù)又分為兩種:命名參數(shù)和位置參數(shù)。這兩種選擇其中之一,不能同時(shí)出現(xiàn)。
必要參數(shù)沒(méi)什么可說(shuō)的,我們來(lái)了解一下可選參數(shù)。
命名參數(shù)
通過(guò)名稱來(lái)傳遞參數(shù),名稱的位置可變動(dòng)。
通過(guò) {parm1, param2, ...}
的形式來(lái)傳遞參數(shù)。
// 定義可選參數(shù)
void enableFlags(String str, {bool blod, bool hidden}) {
print("$str, $blod,$hidden");
}
// 調(diào)用
void main() {
enableFlags("xiaoming",hidden: false, blod: true);
}
位置參數(shù)
使用 []
將一系列參數(shù)包裹起來(lái)作為位置參數(shù)。
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
提供默認(rèn)參數(shù)值
你可以為可選參數(shù)提供默認(rèn)值,默認(rèn)值必須為編譯時(shí)常量,沒(méi)有指定默認(rèn)值的情況下值為 null。
// 提供默認(rèn)參數(shù)
void enableFlags({bool bold = false, bool hidden = false}) {...}
String say(String from, String msg,
[String device = 'carrier pigeon', String mood]) {...}
函數(shù)對(duì)象
函數(shù)可以看作是 Function 類,可以賦值給變量,也可以作為參數(shù)使用。
void main() {
var method = printElement; // 賦值給變量
[1,2,3].forEach(method); // 作為參數(shù)
}
printElement(int e) => print(e);
匿名函數(shù)
匿名函數(shù)是編程語(yǔ)言中關(guān)于函數(shù)的一種重要的變體:沒(méi)有名字的函數(shù),也叫 Lambda 表達(dá)式 或者 Closure 閉包。你可以在各種編程語(yǔ)言中找到它們,可以將匿名函數(shù)直接賦值給變量去使用,或者直接放在參數(shù)列表中使用(通常是最后一個(gè)參數(shù),部分語(yǔ)言稱之為尾隨閉包,有其他變體)。
(參數(shù)列表) {
函數(shù)體;
};
// 使用匿名函數(shù)實(shí)現(xiàn)上個(gè)列子中的效果
[1,2,3].forEach((e) {
print(e);
});
// 或者
[1,2,3].forEach( (e) => print(e) );
注意事項(xiàng)
- 同類的同一個(gè)方法在不同實(shí)例對(duì)象中不相等。
- 所有函數(shù)都有返回值,即使沒(méi)有顯示的返回語(yǔ)句(返回nil)。
運(yùn)算符
運(yùn)算符在編程語(yǔ)言中同樣占據(jù)著重要的地位,Dart 中同樣擁有并遵循的絕大部分的運(yùn)算符效果,例如算術(shù)運(yùn)算符、關(guān)系運(yùn)算符、類型判斷運(yùn)算符、邏輯運(yùn)算符等等。我們主要關(guān)注一些特別的運(yùn)算符的定義和使用場(chǎng)景。
-
算術(shù)運(yùn)算符
~/
: 除并且取整5 ~/ 2
的結(jié)果為 2。 -
類型判斷運(yùn)算符
Dart 作為一門面向?qū)ο笳Z(yǔ)言,類型判斷運(yùn)算符必不可免。常見(jiàn)的
as
類型轉(zhuǎn)換和is
類型判斷。Dart 中有一個(gè)和is
相反的類型判斷運(yùn)算符is!
,當(dāng)對(duì)象是指定類型則返回 false。 -
賦值運(yùn)算符
=、+=、-=
等等這樣的復(fù)合賦值運(yùn)算符在編程語(yǔ)言中非常常見(jiàn),Dart 中有??=
這樣的復(fù)合運(yùn)算符來(lái)為 null 的變量進(jìn)行默認(rèn)值的賦值。a ??= "xiaoming"; // a = a ?? "xiaoming";
-
級(jí)聯(lián)運(yùn)算符
..
可以讓你在同一個(gè)對(duì)象上連續(xù)調(diào)用多個(gè)對(duì)象的變量或者方法。querySelector('#confirm') // 獲取對(duì)象 (Get an object). ..text = 'Confirm' // 使用對(duì)象的成員 (Use its members). ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!'));
-
訪問(wèn)運(yùn)算符
.
和?.
,Dart 通過(guò)點(diǎn)語(yǔ)法訪問(wèn)成員變量或者方法,?.
則會(huì)加上一層判斷,如果操作對(duì)象為 null,則停止訪問(wèn)并返回 null。
異常
Dart中處理異常沒(méi)有太多的差別,只是拋出的對(duì)象可以是任何非 null 對(duì)象而不局限于 Exception 或 Error 類型。
- 拋出異常
throw FormatException('Expected at least 1 section');
- 捕獲異常
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 指定異常
buyMoreLlamas();
} on Exception catch (e) {
// 其它類型的異常
print('Unknown exception: $e');
} catch (e) {
// 不指定類型,處理其它全部
print('Something really unknown: $e');
} finally {
// 總是清理,即便拋出了異常。
cleanLlamaStalls();
}
類
Dart 是支持基于 mixin 繼承機(jī)制的面向?qū)ο笳Z(yǔ)言,所有對(duì)象都是一個(gè)類的實(shí)例,所有的類都繼承自 Object 類。基于 mixin 的繼承意味著除 Object 類之外都只能單繼承。Extension
方法是一種在不更改類型或創(chuàng)建子類的情況下向類添加功能的方法,類似于 Swift 中的 Extension
,OC 中的分類。
構(gòu)造函數(shù)
聲明一個(gè)與類名一樣的函數(shù)即可聲明一個(gè)構(gòu)造函數(shù)。
class Point {
double x, y;
Point(double x, double y) {
this.x = x;
this.y = y;
}
// 或者使用 Dart 提供的語(yǔ)法糖
Point(this.x, this.y);
}
- 如果你沒(méi)有聲明構(gòu)造函數(shù),那么Dart 會(huì)自動(dòng)生成一個(gè)無(wú)參的構(gòu)造函數(shù)并且調(diào)用父類的無(wú)參構(gòu)造方法。
- 構(gòu)造函數(shù)不被繼承,子類不繼承父類的構(gòu)造函數(shù),如果子類不聲明自己的構(gòu)造函數(shù),那么只會(huì)有一個(gè)默認(rèn)無(wú)參的構(gòu)造函數(shù)。
調(diào)用父類非默認(rèn)構(gòu)造函數(shù)
默認(rèn)情況下,本類的構(gòu)造函數(shù)會(huì)調(diào)用父類的無(wú)參構(gòu)造方法,并且先于本類的構(gòu)造函數(shù)之前,如果本類構(gòu)造函數(shù)存在一個(gè)初始化列表,那么調(diào)用父類的無(wú)參構(gòu)造方法會(huì)在該列表的初始化之后,本類的構(gòu)造函數(shù)之前,即下面的順序。
- 本類的初始化列表
- 父類的無(wú)參構(gòu)造函數(shù)
- 本類的構(gòu)造函數(shù)
如果父類沒(méi)有匿名無(wú)參構(gòu)造函數(shù),那么子類就必須在函數(shù)體前使用 :
指定調(diào)用父類其中一個(gè)構(gòu)造函數(shù)。
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// 指定構(gòu)造函數(shù)
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
// in Person
// in Employee
}
初始化列表
在構(gòu)造函數(shù)體之前,我們還可以初始化實(shí)例變量。
Point.fromJson(Map<String, double> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
重定向構(gòu)造函數(shù)
類似于便利構(gòu)造器,我們可以將提供一些默認(rèn)值后,調(diào)用類的其他構(gòu)造器。
class Point {
double x, y;
// 該類的主構(gòu)造函數(shù)。
Point(this.x, this.y);
// 委托實(shí)現(xiàn)給主構(gòu)造函數(shù)。
Point.alongXAxis(double x) : this(x, 0);
}
Getter 和 Setter
Dart 中實(shí)例對(duì)象的每個(gè)屬性都有一個(gè)隱式的 Getter 方法,如果非 final 屬性的話還會(huì)有一個(gè) Setter 方法,你可以使用 get 和 set 關(guān)鍵字為額外的屬性添加 Getter 和 Setter 方法。
class Rectangle {
double left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定義兩個(gè)計(jì)算產(chǎn)生的屬性:right 和 bottom。
double get right => left + width;
set right(double value) => left = value - width;
double get bottom => top + height;
set bottom(double value) => top = value - height;
}
抽象類
使用關(guān)鍵字 abstract
標(biāo)識(shí)符可以讓該類成為抽象類,抽象類一般無(wú)法被實(shí)例化,常用于聲明接口方法,有時(shí)也會(huì)有具體的方法實(shí)現(xiàn)。
abstract class AbstractContainer {
// 定義構(gòu)造函數(shù)、字段、方法等……
void updateChildren(); // 抽象方法。
}
隱式接口
每一個(gè)類都隱式地定義并實(shí)現(xiàn)了該接口,這個(gè)接口包含所有這個(gè)類的實(shí)例成員以及這個(gè)類所有實(shí)現(xiàn)的其他接口。如果你想要?jiǎng)?chuàng)建一個(gè) A 類,它支持所有 B 類中的 API,但是不想繼承 B,那么就可以通過(guò)關(guān)鍵字 implements
實(shí)現(xiàn) B 類中的隱式接口。
class Person {
// _name 變量同樣包含在接口中,但它只是庫(kù)內(nèi)可見(jiàn)的。
final _name;
// 構(gòu)造函數(shù)不在接口中。
Person(this._name);
// greet() 方法在接口中。
String greet(String who) => '你好,$who。我是$_name。';
}
// Person 接口的一個(gè)實(shí)現(xiàn)。
class Impostor implements Person {
get _name => '';
String greet(String who) => '你好$who。你知道我是誰(shuí)嗎?';
}
如果需要實(shí)現(xiàn)多個(gè)類接口,可以使用逗號(hào)分割每個(gè)接口類:
class Point implements Comparable, Location {...}
創(chuàng)建子類
使用 extends
關(guān)鍵字來(lái)創(chuàng)建一個(gè)子類,并可使用 super
關(guān)鍵字引用一個(gè)父類:
class Television {
void turnOn() {
...
}
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
...
}
}
重寫
你可以使用 @override
注解來(lái)表示你重寫來(lái)一個(gè)成員。
class SmartTelevision extends Television {
@override
void turnOn() {...}
}
noSuchMethod()
如果調(diào)用了對(duì)象上不存在的方法或?qū)嵗兞繉?huì)觸發(fā) noSuchMethod
方法,可以通過(guò)重寫該方法來(lái)追蹤和記錄這一行為。
class A {
// 除非你重寫 noSuchMethod,否則調(diào)用一個(gè)不存在的成員會(huì)導(dǎo)致 NoSuchMethodError。
@override
void noSuchMethod(Invocation invocation) {
print('你嘗試使用一個(gè)不存在的成員:' +
'${invocation.memberName}');
}
}
你不能調(diào)用一個(gè)未實(shí)現(xiàn)的方法除非下面其中的一個(gè)條件成立:
- 接收方是靜態(tài)的 dynamic 類型
- 接收方具有靜態(tài)類型,定義了未實(shí)現(xiàn)的方法/抽象方法,并且實(shí)現(xiàn)了 noSuchMethod 方法且實(shí)現(xiàn)與 Object 中的不同
Extension 擴(kuò)展類
extension 是 Dart2.7 引入的,向現(xiàn)有庫(kù)添加功能的一種方式。
// 給 String 添加轉(zhuǎn)換為 Int 的功能
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
main() {
// 使用新的方法
print('123'.parseInt());
}
枚舉類型
一些固定數(shù)量的常量值,Dart 的枚舉功能沒(méi)有特別之處,我們看下常用的方法。
// 定義枚舉
enum Color { red, green, blue }
// 獲取索引
var idx = Color.red.index;
// 獲取所有的類型
List<Color> colors = Color.values;
Dart 中的枚舉類型還有兩個(gè)限制:
- 枚舉不能繼承,也不可以實(shí)現(xiàn)一個(gè)枚舉。
- 不能顯示地實(shí)例化一個(gè)枚舉類。
Mixin 模式
Mixin 是一種在多重繼承中復(fù)用某個(gè)類中代碼的方法模式。Dart 通過(guò)關(guān)鍵字 with
來(lái)使用 Mixin 模式。
mixin Athlete {
run() {
print("run.");
}
}
mixin Singer {
sing() {
print("Sing.");
}
}
class A with Athlete, Singer {
// ...
}
void main() {
var a = A();
a.run();
a.sing();
}
- Minix 類:定一個(gè)類,繼承自 Object 但不為該類定義構(gòu)造函數(shù)
- 單純的 Mixin 類:可以使用關(guān)鍵字 mixin 代替 class
類變量和類方法
類變量和類方法也成為靜態(tài)變量和靜態(tài)方法,它們屬于類而不是某個(gè)實(shí)例變量。
在變量和方法前通過(guò)關(guān)鍵字 static
可以聲明類變量和類方法。
類方法不能被實(shí)例訪問(wèn),因此內(nèi)部也不能使用 this。
泛型
泛型存在于很多編程語(yǔ)言中,例如 Python,Swfit,OC 等,Dart 語(yǔ)言同樣不例外,在之前 List<E> 這樣的聲明,使用到的就是泛型。<...> 符號(hào)表示一個(gè)泛型類,通常使用一個(gè)字母來(lái)表示類型參數(shù),例如E、T、S、K 和 V 等。
泛型常用于需要類型安全的情況,有了它可以更好地生成代碼,減少代碼量。拿 List 來(lái)舉例,List<String>
讓數(shù)組中的元素都必須是字符串類型,當(dāng)你放入非字符串類就會(huì)提前告知你錯(cuò)誤,另外,通過(guò)泛型,我們可以只編寫一份通用的 List 操作就可以適用于多種類型的操作。
類似的,Set 和 Map 同樣可以使用泛型來(lái)約束元素內(nèi)容。
var names = <String>['小蕓', '小芳', '小民'];
var uniqueNames = <String>{'小蕓', '小芳', '小民'};
var pages = <String, String>{
'index.html': '主頁(yè)',
'robots.txt': '網(wǎng)頁(yè)機(jī)器人提示',
'humans.txt': '我們是人類,不是機(jī)器'
};
var nameSet = Set<String>.from(names);
var views = Map<int, View>();
var names = List<String>();
names.addAll(['小蕓', '小芳', '小民']);
限制參數(shù)化類型
有時(shí)想要泛型只支持一定的類型時(shí),可以通過(guò)關(guān)鍵字 extends
來(lái)限制泛型的范圍。
// 支持泛型,但是有約束范圍
class Foo<T extends SomeBaseClass> {...}
// 某類的子類
class Extender extends SomeBaseClass {...}
這樣你在使用 Foo 類時(shí),就可以使用 SomeBaseClass 或者其子類作為泛型參數(shù)。
var a = Foo<SomeBaseClass>();
var b = Foo<Extender>();
var c = Foo<Object>(); // 非SomeBaseClass類型時(shí),將發(fā)生錯(cuò)誤
泛型方法
Dart 中的泛型還可以用在方法中,我們稱使用泛型的方法為泛型方法。
T first<T>(List<T> ts) {
// 處理一些初始化工作或錯(cuò)誤檢測(cè)……
T tmp = ts[0];
// 處理一些額外的檢查……
return tmp;
}
- 方法的返回值為 T 類型
- 參數(shù) ts 為 T 類型的數(shù)組
- 臨時(shí)變量的類型為 T 類型
庫(kù)和可見(jiàn)性
代碼庫(kù)不僅只是提供 API 而且還起到了封裝的作用,庫(kù)中的私有成員通過(guò) _
開(kāi)頭聲明。每個(gè) Dart 程序都是一個(gè)庫(kù)。
導(dǎo)入庫(kù)
對(duì)于 Dart 內(nèi)置庫(kù),使用 dart:xxx
的形式。
import 'dart:html';
而對(duì)于其他的庫(kù),使用系統(tǒng)路徑或者以 package:xxx
的形式 package:xxx 指定的庫(kù)通過(guò)包管理器,如pub工具,來(lái)提供。
import 'package:test/test.dart';
指定前綴
如果導(dǎo)入的庫(kù)有沖突,可以為其中一個(gè)指定前綴。
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// 使用 lib1 的 Element 類。
Element element1 = Element();
// 使用 lib2 的 Element 類。
lib2.Element element2 = lib2.Element();
部分導(dǎo)入
// 導(dǎo)入 foo
import 'package:lib1/lib1.dart' show foo;
// 導(dǎo)入 除了 foo 外的所有
import 'package:lib2/lib2.dart' hide foo;
延遲加載庫(kù)
允許應(yīng)用在需要的時(shí)候才去加載庫(kù)代碼,延遲加載有幾個(gè)好處:
- 減少應(yīng)用的初始化時(shí)間
- 處理 A/B 測(cè)試,比如測(cè)試各種算法的不同實(shí)現(xiàn)
- 加載很少會(huì)使用到的功能
使用 deferred as
關(guān)鍵字來(lái)標(biāo)識(shí)需要延時(shí)加載的代碼庫(kù)。
import 'package:greetings/hello.dart' deferred as hello;
當(dāng)實(shí)際需要使用到庫(kù)中 API 時(shí)先調(diào)用 loadLibrary
函數(shù)加載庫(kù)。
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
- 延遲加載的代碼庫(kù)中的常量需要在代碼庫(kù)被加載的時(shí)候才會(huì)導(dǎo)入,未加載時(shí)候是不會(huì)導(dǎo)入的。
- 導(dǎo)入文件的時(shí)候無(wú)法使用延遲加載庫(kù)中的類型。
- Dart會(huì)隱式地將
loadLibrary
方法導(dǎo)入到使用了deferred as
命名空間 的類中。loadLibrary
函數(shù)返回的是一個(gè)Future
類型。
異步
Dart 通過(guò) async
和 await
關(guān)鍵字實(shí)現(xiàn)異步編程,使用了這些關(guān)鍵字的函數(shù)會(huì)返回 Future
和 Stream
對(duì)象。
- Future 對(duì)象
必須在帶有 async 關(guān)鍵字的異步函數(shù)中使用 await。
// 異步函數(shù)
Future checkVersion() async {
var version = await lookUpVersion(); // 耗時(shí)操作
// 使用 version 繼續(xù)處理
}
你可以在異步函數(shù)中多次使用 await 關(guān)鍵字。
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
await 表達(dá)式的返回值通常是一個(gè) Future 對(duì)象,如果不是 Dart 也會(huì)自動(dòng)將其包裹在一個(gè) Future 對(duì)象里。Future 對(duì)象代表一個(gè)“承諾”,await 表達(dá)式會(huì)阻塞到需要的對(duì)象返回。
- Stream 對(duì)象
想要從 Stream 中獲取值,需要使用 async
關(guān)鍵字或一個(gè)異步循環(huán) await for
。
Future method() async {
await for (var request in requestList) {
// 返回類型為 Stream
handleRequest(request);
}
}
生成器
Dart 中的生成器和迭代器和 Python 中的類似,當(dāng)你需要延遲地生成一連串的值時(shí),可以考慮使用生成器函數(shù)。Dart 內(nèi)置支持兩種形式的生成器方法。
- 同步 生成器:返回一個(gè) Iterable 對(duì)象
- 異步 生成器:返回一個(gè) Stream 對(duì)象
通過(guò)在函數(shù)上加 sync*
關(guān)鍵字并將返回值類型設(shè)置為 Iterable 來(lái)實(shí)現(xiàn)一個(gè) 同步 生成器函數(shù),在函數(shù)中使用 yield
語(yǔ)句來(lái)傳遞值。
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
實(shí)現(xiàn) 異步 生成器函數(shù)與同步類似,只不過(guò)關(guān)鍵字為 async*
并且返回值為 Stream。
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
總結(jié)
- 所有變量引用都是對(duì)象,是類的實(shí)例,包括數(shù)字、函數(shù)以及 null。
- Dart 是強(qiáng)類型語(yǔ)言,變量在聲明是可以指定類型,也可以由Dart自行推斷出類型。
- Dart 支持泛型,List<int> 表示一組由 int 對(duì)象組成的數(shù)組。
- Dart 支持頂級(jí)函數(shù),類方法,類變量等,你還可以進(jìn)行函數(shù)嵌套和局部函數(shù)。
- Dart 的私有屬性通過(guò)
_
開(kāi)頭表示 - Dart 可以顯示警告和錯(cuò)誤兩種類型問(wèn)題。