重要的概念
任何保存在變量中的都是一個(gè) 對(duì)象 , 并且所有的對(duì)象都是對(duì)應(yīng)一個(gè) 類 的實(shí)例示惊。 無論是數(shù)字好港,函數(shù)和
null
都是對(duì)象。所有對(duì)象繼承自 Object 類米罚。盡管 Dart 是強(qiáng)類型的钧汹,但是 Dart 可以推斷類型,所以類型注釋是可選的录择。
var number = 42;
拔莱,number
被推斷為int
類型碗降。 如果要明確說明不需要任何類型, 需要使用特殊類型dynamic
塘秦。Dart 支持泛型讼渊,如
List <int>
(整數(shù)列表)或List <dynamic>
(任何類型的對(duì)象列表)。Dart 支持頂級(jí)函數(shù)(例如
main()
)尊剔, 同樣函數(shù)綁定在類或?qū)ο笊希ǚ謩e是 靜態(tài)函數(shù) 和 實(shí)例函數(shù) )爪幻。 以及支持函數(shù)內(nèi)創(chuàng)建函數(shù) ( 嵌套 或 局部函數(shù) ) 。類似地须误, Dart 支持頂級(jí) 變量 挨稿, 同樣變量綁定在類或?qū)ο笊希o態(tài)變量和實(shí)例變量)。 實(shí)例變量有時(shí)稱為字段或?qū)傩浴?/p>
與 Java 不同霹期,Dart 沒有關(guān)鍵字 “public” 叶组, “protected” 和 “private” 。 如果標(biāo)識(shí)符以下劃線(_)開頭历造,則它相對(duì)于庫是私有的(私有方法必須要抽離在單獨(dú)文件中甩十,否則不生效。)吭产。 有關(guān)更多信息侣监,參考 庫和可見性。
標(biāo)識(shí)符 以字母或下劃線(_)開頭臣淤,后跟任意字母和數(shù)字組合橄霉。
Dart 語法中包含 表達(dá)式( expressions )(有運(yùn)行時(shí)值)和 語句( statements )(沒有運(yùn)行時(shí)值)。 例如邑蒋,條件表達(dá)式
condition ? expr1 : expr2
的值可能是expr1
或expr2
姓蜂。 將其與 if-else 語句 相比較,if-else 語句沒有值医吊。 一條語句通常包含一個(gè)或多個(gè)表達(dá)式钱慢,相反表達(dá)式不能直接包含語句。Dart 工具提示兩種類型問題:警告和錯(cuò)誤卿堂。 警告只是表明代碼可能無法正常工作束莫,但不會(huì)阻止程序的執(zhí)行。 錯(cuò)誤可能是編譯時(shí)錯(cuò)誤或者運(yùn)行時(shí)錯(cuò)誤草描。 編譯時(shí)錯(cuò)誤會(huì)阻止代碼的執(zhí)行; 運(yùn)行時(shí)錯(cuò)誤會(huì)導(dǎo)致代碼在執(zhí)行過程中引發(fā) [異常](#exception)览绿。
語法
變量與常量
1. 變量初始化
// 沒有明確類型,編譯的時(shí)候根據(jù)值明確類型
var name = 'Bob';
dynamic name = 'Bob';
Object name = 'Bob'
// 顯示聲明將被推斷類型, 可以使用String顯示聲明字符串類型
String name = 'Bob' ;
2. 默認(rèn)值
未初始化的變量默認(rèn)值是 null穗慕。即使變量是數(shù)字 類型默認(rèn)值也是 null饿敲,因?yàn)樵?Dart 中一切都是對(duì)象,數(shù)字類型 也不例外逛绵。
3. Object 和 dynamic
dynamic a = 'string';
a = 10;
a = 1.1;
a.foo();
Object b = 'string';
b = 10;
b = 1.1;
// 編譯錯(cuò)誤 "The method 'foo' isn't defined for the type 'Object'."
// b.foo();
- dynamic和object類型是可以變的
- Object 是靜態(tài)類型檢測的诀蓉,所以O(shè)bject 對(duì)象只能調(diào)用Object 的方法栗竖,調(diào)用其他方法會(huì)產(chǎn)生編譯錯(cuò)誤
- dynamic是運(yùn)行時(shí)檢測的,所以可以調(diào)用任何方法渠啤,(注意會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤,盡量避免使用)
4. Final 和 Const
- 使用過程中從來不會(huì)被修改的變量添吗, 可以使用 final 或 const
- 實(shí)例變量可以是 final 類型但不能是 const 類型沥曹。 必須在構(gòu)造函數(shù)體執(zhí)行之前初始化 final 實(shí)例變量
- Final 變量的值只能被設(shè)置一次, 最高級(jí) final 變量或類變量在第一次使用時(shí)被初始化(運(yùn)行時(shí))
class Name {
// 1. 直接設(shè)置默認(rèn)值
final a = 10;
// 2. 在初始化列表中初始化
final a;
Name(this.a)
}
- Const 變量在編譯時(shí)就已經(jīng)固定 (Const 變量 是隱式 Final 的類型.)
- Const 變量必須由常量初始化
var a = 10;
const b = 10;
// 編譯報(bào)錯(cuò) "Const variables must be initialized with a constant value."
const c = a;
const d = b;
- 在集合字面量之前添加 const 關(guān)鍵字,可以定義編譯時(shí)常量
var a = const [1, 2, 3];
var b = const {1, 2, 3};
var c = const {1: 1, 2: 2};
內(nèi)建類型
- Dart 語言支持以下內(nèi)建類型:
- Number
- String
- Boolean
- List (也被稱為 Array)
- Map
- Set
- Rune (用于在字符串中表示 Unicode 字符)
- Symbol
1. Number
- Dart 語言的 Number 有兩種類型: int double
- int 整數(shù)值不大于64位碟联, 具體取決于平臺(tái)
- double 64位(雙精度)浮點(diǎn)數(shù)
2. String
- Dart 字符串是一組 UTF-16 單元序列妓美。 字符串通過單引號(hào)或者雙引號(hào)創(chuàng)建。
- 字符串可以通過 ${expression} 的方式內(nèi)嵌表達(dá)式鲤孵。 如果表達(dá)式是一個(gè)標(biāo)識(shí)符壶栋,則 {} 可以省略。
- Flutter中字符串格式化只有插值普监,可以借助第三方庫
sprintf
來實(shí)現(xiàn)格式化字符串
3. Boolean
- Dart 使用 bool 類型表示布爾值贵试。 Dart 只有字面量 true and false 是布爾類型, 這兩個(gè)對(duì)象都是編譯時(shí)常量凯正。
- Dart 的類型安全意味著不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)毙玻。
3. Set
var names = Set();
var names = Set<String>();
var names = <String>{};
// var names = {}; // 這樣會(huì)創(chuàng)建一個(gè) Map ,而不是 Set 廊散。
函數(shù)
函數(shù)也是對(duì)象桑滩,并且有它的類型 Function。這也意味著函數(shù)可以被賦值給變量或者作為參數(shù)傳遞給其他函數(shù)允睹。 也可以把 Dart 類的實(shí)例當(dāng)做方法來調(diào)用运准。
- 如果函數(shù)中只有一句表達(dá)式,可以使用簡寫語法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
- 在箭頭 (=>) 和分號(hào) (;) 之間只能使用一個(gè) 表達(dá)式 缭受,不能是 語句胁澳。
- 函數(shù)有兩種參數(shù)類型: required 和 optional。 required 類型參數(shù)在參數(shù)最前面贯涎, 隨后是 optional 類型參數(shù)听哭。 命名的可選參數(shù)也可以標(biāo)記為 “@ required”
- 所有函數(shù)都有返回值,沒有明確指定返回值塘雳,函數(shù)隱式添加
return null;
1. 命名可選參數(shù)
- 定義函數(shù)是陆盘,使用 {param1, param2, …} 來指定命名參數(shù)
void enableFlags({bool bold, bool hidden}) {...}
- 調(diào)用函數(shù)時(shí),可以使用指定命名參數(shù) paramName: value,
enableFlags(bold: true, hidden: false);
2. 位置可選參數(shù)
- 定義函數(shù)是败明,使用 [param1, param2, …] 來指定位置可選參數(shù)
void enableFlags([bool bold, bool hidden]) {...}
- 調(diào)用函數(shù), 自動(dòng)按順序?qū)?shù)據(jù)賦值給位置參數(shù)
enableFlags(true, true);
3. 默認(rèn)參數(shù)值
在定義方法的時(shí)候隘马,可以使用 = 來定義可選參數(shù)的默認(rèn)值。 默認(rèn)值只能是編譯時(shí)常量妻顶。 如果沒有提供默認(rèn)值酸员,則默認(rèn)值為 null蜒车。
- 可選參數(shù)才能設(shè)置默認(rèn)值
// 位置可選
void enableFlags([bool bold = false, bool hidden = false]) { ... }
// 命名可選
void enableFlags({bool bold = false, bool hidden = false}) { ... }
- 位置可選參數(shù)和命名可選參數(shù)不能混用。
4. 匿名函數(shù)(block)
// 1
var testFunc = (String a) { ... };
// 2
Function(String a) testFunc;
testFunc = (String a) { ... };
// 3
bool Function(String a) testFunc;
testFunc = (String a) { ... };
// 4
typedef MyFunc = bool Function(String a);
MyFunc testFunc;
運(yùn)算符
- 級(jí)聯(lián)運(yùn)算符 (..) 可以實(shí)現(xiàn)對(duì)同一個(gè)對(duì)像進(jìn)行一系列的操作幔嗦。 除了調(diào)用函數(shù)酿愧, 還可以訪問同一對(duì)象上的字段屬性。 這通逞可以節(jié)省創(chuàng)建臨時(shí)變量的步驟嬉挡, 同時(shí)編寫出更流暢的代碼。
querySelector('#confirm') // 獲取對(duì)象汇恤。
..text = 'Confirm' // 調(diào)用成員變量庞钢。
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
- switch 和 case
在非空 case必須加break、continue因谎、thow或return
空case允許程序以 fall-through 的形式執(zhí)行
非空case 實(shí)現(xiàn) fall-through 需要使用 continue 語句結(jié)合 lable 的方式實(shí)現(xiàn):
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;
}
異常
- Dart 中的所有異常是非檢查異常基括。 方法不會(huì)聲明它們拋出的異常, 也不要求捕獲任何異常财岔。
- Dart 提供了 Exception 和 Error 類型风皿, 以及一些子類型。 當(dāng)然也可以定義自己的異常類型使鹅。 但是揪阶,此外 Dart 程序可以拋出任何非 null 對(duì)象, 不僅限 Exception 和 Error 對(duì)象患朱。
// 拋出異常
throw FormatException('Expected at least 1 section');
throw 'Out of llamas!';
捕獲
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
- 捕獲語句中可以同時(shí)使用 on 和 catch 鲁僚,也可以單獨(dú)分開使用。 使用 on 來指定異常類型裁厅, 使用 catch 來 捕獲異常對(duì)象冰沙。
-
catch()
函數(shù)可以指定1到2個(gè)參數(shù), 第一個(gè)參數(shù)為拋出的異常對(duì)象执虹, 第二個(gè)為堆棧信息 ( 一個(gè) StackTrace 對(duì)象 )拓挥。
類
1. 構(gòu)造函數(shù)
- 通過 構(gòu)造函數(shù) 創(chuàng)建對(duì)象。 構(gòu)造函數(shù)的名字可以是 ClassName 或者 ClassName.identifier袋励。
class Point {
final x;
final y;
Point(this.x, this.y);
Point.fromJson(this.x, this.y);
}
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
- 構(gòu)造函數(shù)實(shí)例變量賦值簡寫
class Point {
num x, y;
// 1 手動(dòng)初始化屬性
Point(num x, num y) {
// 還有更好的方式來實(shí)現(xiàn)下面代碼侥啤,敬請(qǐng)關(guān)注。
this.x = x;
this.y = y;
}
// 2 自動(dòng)初始化屬性
Point(this.x, this.y)
// 3. 初始化列表
Point(int x, int y) : this.x = x, this.y = y;
}
- 在沒有聲明構(gòu)造函數(shù)的情況下茬故, Dart 會(huì)提供一個(gè)默認(rèn)的構(gòu)造函數(shù)盖灸。 默認(rèn)構(gòu)造函數(shù)沒有參數(shù)并會(huì)調(diào)用父類的無參構(gòu)造函數(shù)。
- 子類不會(huì)繼承父類的構(gòu)造函數(shù)磺芭。 子類不聲明構(gòu)造函數(shù)赁炎,那么它就只有默認(rèn)構(gòu)造函數(shù) (匿名,沒有參數(shù)) 钾腺。
- 使用命名構(gòu)造函數(shù)可為一個(gè)類實(shí)現(xiàn)多個(gè)構(gòu)造函數(shù)徙垫, 也可以使用命名構(gòu)造函數(shù)來更清晰的表明函數(shù)意圖
- 常量構(gòu)造函數(shù)
const Point.ImmutablePoint(this.x, this.y);
var a = const Point.ImmutablePoint(10, 11);
var b = const Point.ImmutablePoint(10, 11);
print(identical(a, b)); // true
包含常量構(gòu)造函數(shù)的類中只能包含final
屬性
在常量構(gòu)造函數(shù)前加const
會(huì)創(chuàng)建出唯一編譯時(shí)常量
- 工廠構(gòu)造函數(shù)(可以手動(dòng)返回一個(gè)對(duì)象)
當(dāng)執(zhí)行構(gòu)造函數(shù)并不總是創(chuàng)建這個(gè)類的一個(gè)新實(shí)例時(shí)讥裤,則使用 factory 關(guān)鍵字。 例如姻报,一個(gè)工廠構(gòu)造函數(shù)可能會(huì)返回一個(gè) cache 中的實(shí)例己英, 或者可能返回一個(gè)子類的實(shí)例。
以下示例演示了從緩存中返回對(duì)象的工廠構(gòu)造函數(shù)(工廠構(gòu)造函數(shù)無法訪問 this逗抑。):
class Logger {
final String name;
bool mute = false;
// 從命名的 _ 可以知剧辐,
// _cache 是私有屬性。
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
使用工廠方法實(shí)現(xiàn)單例
Singleton._privateConstructor();
static final Singleton _instance = Singleton._privateConstructor();
factory Singleton(){
return _instance;
}
工廠方法 只是為了手動(dòng)返回一個(gè)實(shí)例, 下面兩種實(shí)現(xiàn)等價(jià)邮府。
// 工廠方法 只是為了手動(dòng)返回一個(gè)實(shí)例
class Test {
Test._internal();
static final Test _instance = Test._internal();
// 1.
static Test init1() {
return _instance;
}
// 2.
factory Test.init2() {
return _instance;
}
}
2. 調(diào)用父類非默認(rèn)構(gòu)造函數(shù)
默認(rèn)情況下,子類的構(gòu)造函數(shù)會(huì)自動(dòng)調(diào)用父類的默認(rèn)構(gòu)造函數(shù)(匿名溉奕,無參數(shù))褂傀。 父類的構(gòu)造函數(shù)在子類構(gòu)造函數(shù)體開始執(zhí)行的位置被調(diào)用。 如果提供了一個(gè) initializer list (初始化參數(shù)列表)加勤, 則初始化參數(shù)列表在父類構(gòu)造函數(shù)執(zhí)行之前執(zhí)行仙辟。 總之,執(zhí)行順序如下(類似于Swift 的兩段式初始化):
- initializer list (初始化參數(shù)列表)
- superclass’s no-arg constructor (父類的無名構(gòu)造函數(shù))
- main class’s no-arg constructor (主類的無名構(gòu)造函數(shù))
如果父類中沒有匿名無參的構(gòu)造函數(shù)鳄梅, 則需要手工調(diào)用父類的其他構(gòu)造函數(shù)叠国。 在當(dāng)前構(gòu)造函數(shù)冒號(hào) (:) 之后丑孩,函數(shù)體之前壹瘟,聲明調(diào)用父類構(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');
}
}
3. 實(shí)例變量
class Point {
num x; // 聲明示例變量 x暑竟,初始值為 null 揽祥。
num y; // 聲明示例變量 y赐俗,初始值為 null 几苍。
num z = 0; // 聲明示例變量 z蚀浆,初始值為 0 挎峦。
}
- 未初始化實(shí)例變量的默認(rèn)人值為 “null” 香追。
- 所有實(shí)例變量都生成隱式 getter 方法。 非 final 的實(shí)例變量同樣會(huì)生成隱式 setter 方法坦胶。 有關(guān)更多信息透典,參考 Getters 和 setters.
- 如果在聲明時(shí)進(jìn)行了實(shí)例變量的初始化, 那么初始化值會(huì)在實(shí)例創(chuàng)建時(shí)賦值給變量顿苇, 該賦值過程在構(gòu)造函數(shù)及其初始化列表執(zhí)行之前峭咒。
4. Getter 和 Setter
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定義兩個(gè)計(jì)算屬性: right 和 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;
}
5. 抽象類
abstract class Doer {
// 定義實(shí)例變量和方法 ...
void doSomething(); // 定義一個(gè)抽象方法岖圈。
}
- 抽象類不能實(shí)例化
- 抽象類中包含抽象方法讹语,普通方法。抽象方法必須被重寫
6. 隱式接口
在Dart 中沒有interface蜂科。每個(gè)Class都是一個(gè)隱式接口顽决。
使用implements
實(shí)現(xiàn)接口
// person 類短条。 隱式接口里面包含了 greet() 方法聲明。
class Person {
// 包含在接口里才菠,但只在當(dāng)前庫中可見茸时。
final _name;
// 不包含在接口里,因?yàn)檫@是一個(gè)構(gòu)造函數(shù)赋访。
Person(this._name);
// 包含在接口里可都。
String greet(String who) => 'Hello, $who. I am $_name.';
}
// person 接口的實(shí)現(xiàn)。
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
實(shí)現(xiàn)多個(gè)接口
class Point implements Comparable, Location {...}
7. 繼承
- Dart 是單繼承蚓耽。使用 extends 關(guān)鍵字來創(chuàng)建子類渠牲, 使用 super 關(guān)鍵字來引用父類:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
- 子類可以重寫實(shí)例方法,getter 和 setter步悠。 可以使用 @override 注解指出想要重寫的成員:
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
8. 為類添加功能: Mixin
Mixin 是復(fù)用類代碼的一種途徑签杈, 復(fù)用的類可以在不同層級(jí),之間可以不存在繼承關(guān)系鼎兽。
- 通過 with 后面跟一個(gè)或多個(gè)混入的名稱答姥,來 使用 Mixin , 下面的示例演示了兩個(gè)使用 Mixin 的類:
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
- 通過創(chuàng)建一個(gè)繼承自 Object 且沒有構(gòu)造函數(shù)的類谚咬,來 實(shí)現(xiàn) 一個(gè) Mixin 鹦付。 如果 Mixin 不希望作為常規(guī)類被使用,使用關(guān)鍵字 mixin 替換 class 择卦。 例如:
mixin 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');
}
}
}
- 指定只有某些類型可以使用的 Mixin - 比如敲长, Mixin 可以調(diào)用 Mixin 自身沒有定義的方法 - 使用 on 來指定可以使用 Mixin 的父類類型:
mixin MusicalPerformer on Musician {
// ···
}
9. noSuchMethod()
當(dāng)代碼嘗試使用不存在的方法或?qū)嵗兞繒r(shí), 通過重寫 noSuchMethod() 方法互捌,來實(shí)現(xiàn)檢測和應(yīng)對(duì)處理:
class A {
// 如果不重寫 noSuchMethod潘明,訪問
// 不存在的實(shí)例變量時(shí)會(huì)導(dǎo)致 NoSuchMethodError 錯(cuò)誤。
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
除非符合下面的任意一項(xiàng)條件秕噪, 否則沒有實(shí)現(xiàn)的方法不能夠被調(diào)用:
- receiver 具有 dynamic 的靜態(tài)類型 钳降。
- receiver 具有靜態(tài)類型,用于定義為實(shí)現(xiàn)的方法 (可以是抽象的), 并且 receiver 的動(dòng)態(tài)類型具有 noSuchMethod() 的實(shí)現(xiàn)腌巾, 該實(shí)現(xiàn)與 Object 類中的實(shí)現(xiàn)不同遂填。
枚舉
枚舉中的每個(gè)值都有一個(gè) index getter 方法, 該方法返回值所在枚舉類型定義中的位置(從 0 開始)澈蝙。 例如吓坚,第一個(gè)枚舉值的索引是 0 , 第二個(gè)枚舉值的索引是 1灯荧。
枚舉類型具有以下限制:
- 枚舉不能被子類化礁击,混合或?qū)崿F(xiàn)。
- 枚舉不能被顯式實(shí)例化。