重要的概念
任何保存在變量中的都是一個 對象 , 并且所有的對象都是對應一個 類 的實例。 無論是數(shù)字,函數(shù)和
null都是對象。所有對象繼承自 Object 類。盡管 Dart 是強類型的,但是 Dart 可以推斷類型,所以類型注釋是可選的。
var number = 42;,number被推斷為int類型。 如果要明確說明不需要任何類型, 需要使用特殊類型dynamic。Dart 支持泛型,如
List <int>(整數(shù)列表)或List <dynamic>(任何類型的對象列表)。Dart 支持頂級函數(shù)(例如
main()), 同樣函數(shù)綁定在類或?qū)ο笊希ǚ謩e是 靜態(tài)函數(shù) 和 實例函數(shù) )。 以及支持函數(shù)內(nèi)創(chuàng)建函數(shù) ( 嵌套 或 局部函數(shù) ) 。類似地, Dart 支持頂級 變量 , 同樣變量綁定在類或?qū)ο笊希o態(tài)變量和實例變量)。 實例變量有時稱為字段或?qū)傩浴?/p>
與 Java 不同,Dart 沒有關(guān)鍵字 “public” , “protected” 和 “private” 。 如果標識符以下劃線(_)開頭,則它相對于庫是私有的(私有方法必須要抽離在單獨文件中,否則不生效。)。 有關(guān)更多信息,參考 庫和可見性。
標識符 以字母或下劃線(_)開頭,后跟任意字母和數(shù)字組合。
Dart 語法中包含 表達式( expressions )(有運行時值)和 語句( statements )(沒有運行時值)。 例如,條件表達式
condition ? expr1 : expr2的值可能是expr1或expr2。 將其與 if-else 語句 相比較,if-else 語句沒有值。 一條語句通常包含一個或多個表達式,相反表達式不能直接包含語句。Dart 工具提示兩種類型問題:警告和錯誤。 警告只是表明代碼可能無法正常工作,但不會阻止程序的執(zhí)行。 錯誤可能是編譯時錯誤或者運行時錯誤。 編譯時錯誤會阻止代碼的執(zhí)行; 運行時錯誤會導致代碼在執(zhí)行過程中引發(fā) [異常](#exception)。
語法
變量與常量
1. 變量初始化
// 沒有明確類型,編譯的時候根據(jù)值明確類型
var name = 'Bob';
dynamic name = 'Bob';
Object name = 'Bob'
// 顯示聲明將被推斷類型, 可以使用String顯示聲明字符串類型
String name = 'Bob' ;
2. 默認值
未初始化的變量默認值是 null。即使變量是數(shù)字 類型默認值也是 null,因為在 Dart 中一切都是對象,數(shù)字類型 也不例外。
3. Object 和 dynamic
dynamic a = 'string';
a = 10;
a = 1.1;
a.foo();
Object b = 'string';
b = 10;
b = 1.1;
// 編譯錯誤 "The method 'foo' isn't defined for the type 'Object'."
// b.foo();
- dynamic和object類型是可以變的
- Object 是靜態(tài)類型檢測的,所以Object 對象只能調(diào)用Object 的方法,調(diào)用其他方法會產(chǎn)生編譯錯誤
- dynamic是運行時檢測的,所以可以調(diào)用任何方法,(注意會產(chǎn)生運行時錯誤,盡量避免使用)
4. Final 和 Const
- 使用過程中從來不會被修改的變量, 可以使用 final 或 const
- 實例變量可以是 final 類型但不能是 const 類型。 必須在構(gòu)造函數(shù)體執(zhí)行之前初始化 final 實例變量
- Final 變量的值只能被設置一次, 最高級 final 變量或類變量在第一次使用時被初始化(運行時)
class Name {
// 1. 直接設置默認值
final a = 10;
// 2. 在初始化列表中初始化
final a;
Name(this.a)
}
- Const 變量在編譯時就已經(jīng)固定 (Const 變量 是隱式 Final 的類型.)
- Const 變量必須由常量初始化
var a = 10;
const b = 10;
// 編譯報錯 "Const variables must be initialized with a constant value."
const c = a;
const d = b;
- 在集合字面量之前添加 const 關(guān)鍵字,可以定義編譯時常量
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位, 具體取決于平臺
- double 64位(雙精度)浮點數(shù)
2. String
- Dart 字符串是一組 UTF-16 單元序列。 字符串通過單引號或者雙引號創(chuàng)建。
- 字符串可以通過 ${expression} 的方式內(nèi)嵌表達式。 如果表達式是一個標識符,則 {} 可以省略。
- Flutter中字符串格式化只有插值,可以借助第三方庫
sprintf來實現(xiàn)格式化字符串
3. Boolean
- Dart 使用 bool 類型表示布爾值。 Dart 只有字面量 true and false 是布爾類型, 這兩個對象都是編譯時常量。
- Dart 的類型安全意味著不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)。
3. Set
var names = Set();
var names = Set<String>();
var names = <String>{};
// var names = {}; // 這樣會創(chuàng)建一個 Map ,而不是 Set 。
函數(shù)
函數(shù)也是對象,并且有它的類型 Function。這也意味著函數(shù)可以被賦值給變量或者作為參數(shù)傳遞給其他函數(shù)。 也可以把 Dart 類的實例當做方法來調(diào)用。
- 如果函數(shù)中只有一句表達式,可以使用簡寫語法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
- 在箭頭 (=>) 和分號 (;) 之間只能使用一個 表達式 ,不能是 語句。
- 函數(shù)有兩種參數(shù)類型: required 和 optional。 required 類型參數(shù)在參數(shù)最前面, 隨后是 optional 類型參數(shù)。 命名的可選參數(shù)也可以標記為 “@ required”
- 所有函數(shù)都有返回值,沒有明確指定返回值,函數(shù)隱式添加
return null;
1. 命名可選參數(shù)
- 定義函數(shù)是,使用 {param1, param2, …} 來指定命名參數(shù)
void enableFlags({bool bold, bool hidden}) {...}
- 調(diào)用函數(shù)時,可以使用指定命名參數(shù) paramName: value,
enableFlags(bold: true, hidden: false);
2. 位置可選參數(shù)
- 定義函數(shù)是,使用 [param1, param2, …] 來指定位置可選參數(shù)
void enableFlags([bool bold, bool hidden]) {...}
- 調(diào)用函數(shù), 自動按順序?qū)?shù)據(jù)賦值給位置參數(shù)
enableFlags(true, true);
3. 默認參數(shù)值
在定義方法的時候,可以使用 = 來定義可選參數(shù)的默認值。 默認值只能是編譯時常量。 如果沒有提供默認值,則默認值為 null。
- 可選參數(shù)才能設置默認值
// 位置可選
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;
運算符
- 級聯(lián)運算符 (..) 可以實現(xiàn)對同一個對像進行一系列的操作。 除了調(diào)用函數(shù), 還可以訪問同一對象上的字段屬性。 這通常可以節(jié)省創(chuàng)建臨時變量的步驟, 同時編寫出更流暢的代碼。
querySelector('#confirm') // 獲取對象。
..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 實現(xiàn) fall-through 需要使用 continue 語句結(jié)合 lable 的方式實現(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 中的所有異常是非檢查異常。 方法不會聲明它們拋出的異常, 也不要求捕獲任何異常。
- Dart 提供了 Exception 和 Error 類型, 以及一些子類型。 當然也可以定義自己的異常類型。 但是,此外 Dart 程序可以拋出任何非 null 對象, 不僅限 Exception 和 Error 對象。
// 拋出異常
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();
}
- 捕獲語句中可以同時使用 on 和 catch ,也可以單獨分開使用。 使用 on 來指定異常類型, 使用 catch 來 捕獲異常對象。
-
catch()函數(shù)可以指定1到2個參數(shù), 第一個參數(shù)為拋出的異常對象, 第二個為堆棧信息 ( 一個 StackTrace 對象 )。
類
1. 構(gòu)造函數(shù)
- 通過 構(gòu)造函數(shù) 創(chuàng)建對象。 構(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ù)實例變量賦值簡寫
class Point {
num x, y;
// 1 手動初始化屬性
Point(num x, num y) {
// 還有更好的方式來實現(xiàn)下面代碼,敬請關(guān)注。
this.x = x;
this.y = y;
}
// 2 自動初始化屬性
Point(this.x, this.y)
// 3. 初始化列表
Point(int x, int y) : this.x = x, this.y = y;
}
- 在沒有聲明構(gòu)造函數(shù)的情況下, Dart 會提供一個默認的構(gòu)造函數(shù)。 默認構(gòu)造函數(shù)沒有參數(shù)并會調(diào)用父類的無參構(gòu)造函數(shù)。
- 子類不會繼承父類的構(gòu)造函數(shù)。 子類不聲明構(gòu)造函數(shù),那么它就只有默認構(gòu)造函數(shù) (匿名,沒有參數(shù)) 。
- 使用命名構(gòu)造函數(shù)可為一個類實現(xiàn)多個構(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 會創(chuàng)建出唯一編譯時常量
- 工廠構(gòu)造函數(shù)(可以手動返回一個對象)
當執(zhí)行構(gòu)造函數(shù)并不總是創(chuàng)建這個類的一個新實例時,則使用 factory 關(guān)鍵字。 例如,一個工廠構(gòu)造函數(shù)可能會返回一個 cache 中的實例, 或者可能返回一個子類的實例。
以下示例演示了從緩存中返回對象的工廠構(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);
}
}
使用工廠方法實現(xiàn)單例
Singleton._privateConstructor();
static final Singleton _instance = Singleton._privateConstructor();
factory Singleton(){
return _instance;
}
工廠方法 只是為了手動返回一個實例, 下面兩種實現(xiàn)等價。
// 工廠方法 只是為了手動返回一個實例
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)用父類非默認構(gòu)造函數(shù)
默認情況下,子類的構(gòu)造函數(shù)會自動調(diào)用父類的默認構(gòu)造函數(shù)(匿名,無參數(shù))。 父類的構(gòu)造函數(shù)在子類構(gòu)造函數(shù)體開始執(zhí)行的位置被調(diào)用。 如果提供了一個 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ù)。 在當前構(gòu)造函數(shù)冒號 (:) 之后,函數(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. 實例變量
class Point {
num x; // 聲明示例變量 x,初始值為 null 。
num y; // 聲明示例變量 y,初始值為 null 。
num z = 0; // 聲明示例變量 z,初始值為 0 。
}
- 未初始化實例變量的默認人值為 “null” 。
- 所有實例變量都生成隱式 getter 方法。 非 final 的實例變量同樣會生成隱式 setter 方法。 有關(guān)更多信息,參考 Getters 和 setters.
- 如果在聲明時進行了實例變量的初始化, 那么初始化值會在實例創(chuàng)建時賦值給變量, 該賦值過程在構(gòu)造函數(shù)及其初始化列表執(zhí)行之前。
4. Getter 和 Setter
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定義兩個計算屬性: 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 {
// 定義實例變量和方法 ...
void doSomething(); // 定義一個抽象方法。
}
- 抽象類不能實例化
- 抽象類中包含抽象方法,普通方法。抽象方法必須被重寫
6. 隱式接口
在Dart 中沒有interface。每個Class都是一個隱式接口。
使用implements 實現(xiàn)接口
// person 類。 隱式接口里面包含了 greet() 方法聲明。
class Person {
// 包含在接口里,但只在當前庫中可見。
final _name;
// 不包含在接口里,因為這是一個構(gòu)造函數(shù)。
Person(this._name);
// 包含在接口里。
String greet(String who) => 'Hello, $who. I am $_name.';
}
// person 接口的實現(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()));
}
實現(xiàn)多個接口
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();
}
// ···
}
- 子類可以重寫實例方法,getter 和 setter。 可以使用 @override 注解指出想要重寫的成員:
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
8. 為類添加功能: Mixin
Mixin 是復用類代碼的一種途徑, 復用的類可以在不同層級,之間可以不存在繼承關(guān)系。
- 通過 with 后面跟一個或多個混入的名稱,來 使用 Mixin , 下面的示例演示了兩個使用 Mixin 的類:
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
- 通過創(chuàng)建一個繼承自 Object 且沒有構(gòu)造函數(shù)的類,來 實現(xiàn) 一個 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()
當代碼嘗試使用不存在的方法或?qū)嵗兞繒r, 通過重寫 noSuchMethod() 方法,來實現(xiàn)檢測和應對處理:
class A {
// 如果不重寫 noSuchMethod,訪問
// 不存在的實例變量時會導致 NoSuchMethodError 錯誤。
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
除非符合下面的任意一項條件, 否則沒有實現(xiàn)的方法不能夠被調(diào)用:
- receiver 具有 dynamic 的靜態(tài)類型 。
- receiver 具有靜態(tài)類型,用于定義為實現(xiàn)的方法 (可以是抽象的), 并且 receiver 的動態(tài)類型具有 noSuchMethod() 的實現(xiàn), 該實現(xiàn)與 Object 類中的實現(xiàn)不同。
枚舉
枚舉中的每個值都有一個 index getter 方法, 該方法返回值所在枚舉類型定義中的位置(從 0 開始)。 例如,第一個枚舉值的索引是 0 , 第二個枚舉值的索引是 1。
枚舉類型具有以下限制:
- 枚舉不能被子類化,混合或?qū)崿F(xiàn)。
- 枚舉不能被顯式實例化。