Dart 是一種基于類和 mixin 繼承機(jī)制的面向?qū)ο蟮恼Z言。 每個(gè)對象都是一個(gè)類的實(shí)例,所有的類都繼承于 Object. 。 基于 * Mixin 繼承* 意味著每個(gè)類(除 Object 外) 都只有一個(gè)超類, 一個(gè)類中的代碼可以在其他多個(gè)繼承類中重復(fù)使用。
對象是由函數(shù)和數(shù)據(jù)(即方法和實(shí)例變量)組成。 方法的調(diào)用要通過對象來完成: 調(diào)用的方法可以訪問其對象的其他函數(shù)和數(shù)據(jù)。
使用 (.) 來引用實(shí)例對象的變量和方法:
var p = Point(2, 2);
// 為實(shí)例的變量 y 設(shè)置值。
p.y = 3;
// 獲取變量 y 的值。
assert(p.y == 3);
// 調(diào)用 p 的 distanceTo() 方法。
num distance = p.distanceTo(Point(4, 4));
使用 ?. 來代替 . , 可以避免因?yàn)樽筮厡ο罂赡転?null , 導(dǎo)致的異常:
// 如果 p 為 non-null,設(shè)置它變量 y 的值為 4。
p?.y = 4;
通過 構(gòu)造函數(shù) 創(chuàng)建對象。 構(gòu)造函數(shù)的名字可以是 ClassName 或者 ClassName.identifier。例如, 以下代碼使用 Point 和 Point.fromJson() 構(gòu)造函數(shù)創(chuàng)建 Point 對象:
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
以下代碼具有相同的效果, 但是構(gòu)造函數(shù)前面的的 new 關(guān)鍵字是可選的:
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
版本提示: 在 Dart 2 中 new
關(guān)鍵字變成了可選的。
一些類提供了常量構(gòu)造函數(shù)。 使用常量構(gòu)造函數(shù),在構(gòu)造函數(shù)名之前加 const 關(guān)鍵字,來創(chuàng)建編譯時(shí)常量時(shí):
var p = const ImmutablePoint(2, 2);
構(gòu)造兩個(gè)相同的編譯時(shí)常量會產(chǎn)生一個(gè)唯一的, 標(biāo)準(zhǔn)的實(shí)例:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // 它們是同一個(gè)實(shí)例。
在 常量上下文 中, 構(gòu)造函數(shù)或者字面量前的 const 可以省略。 例如,下面代碼創(chuàng)建了一個(gè) const 類型的 map 對象:
// 這里有很多的 const 關(guān)鍵字。
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
保留第一個(gè) const 關(guān)鍵字,其余的全部省略:
// 僅有一個(gè) const ,由該 const 建立常量上下文。
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
如果常量構(gòu)造函數(shù)在常量上下文之外, 且省略了 const 關(guān)鍵字, 此時(shí)創(chuàng)建的對象是非常量對象:
var a = const ImmutablePoint(1, 1); // 創(chuàng)建一個(gè)常量對象
var b = ImmutablePoint(1, 1); // 創(chuàng)建一個(gè)非常量對象
assert(!identical(a, b)); // 兩者不是同一個(gè)實(shí)例!
版本提示: 在 Dart 2 中,一個(gè)常量上下文中的 const
關(guān)鍵字可以被省略。
使用對象的 runtimeType 屬性, 可以在運(yùn)行時(shí)獲取對象的類型, runtimeType 屬性回返回一個(gè) Type 對象。
print('The type of a is ${a.runtimeType}');
到目前為止,我們已經(jīng)解了如何_使用_類。 本節(jié)的其余部分將介紹如何_實(shí)現(xiàn)_一個(gè)類。
下面是聲明實(shí)例變量的示例:
class Point {
num x; // 聲明示例變量 x,初始值為 null 。
num y; // 聲明示例變量 y,初始值為 null 。
num z = 0; // 聲明示例變量 z,初始值為 0 。
}
未初始化實(shí)例變量的默認(rèn)人值為 “null” 。
所有實(shí)例變量都生成隱式 getter 方法。 非 final 的實(shí)例變量同樣會生成隱式 setter 方法。 有關(guān)更多信息,參考 Getters 和 setters.
class Point {
num x;
num y;
}
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}
如果在聲明時(shí)進(jìn)行了示例變量的初始化, 那么初始化值會在示例創(chuàng)建時(shí)賦值給變量, 該賦值過程在構(gòu)造函數(shù)及其初始化列表執(zhí)行之前。
通過創(chuàng)建一個(gè)與其類同名的函數(shù)來聲明構(gòu)造函數(shù) (另外,還可以附加一個(gè)額外的可選標(biāo)識符,如 命名構(gòu)造函數(shù) 中所述)。 下面通過最常見的構(gòu)造函數(shù)形式, 即生成構(gòu)造函數(shù), 創(chuàng)建一個(gè)類的實(shí)例:
class Point {
num x, y;
Point(num x, num y) {
// 還有更好的方式來實(shí)現(xiàn)下面代碼,敬請關(guān)注。
this.x = x;
this.y = y;
}
}
使用 this 關(guān)鍵字引用當(dāng)前實(shí)例。
提示: 近當(dāng)存在命名沖突時(shí),使用 this 關(guān)鍵字。 否則,按照 Dart 風(fēng)格應(yīng)該省略 this 。
通常模式下,會將構(gòu)造函數(shù)傳入的參數(shù)的值賦值給對應(yīng)的實(shí)例變量, Dart 自身的語法糖精簡了這些代碼:
class Point {
num x, y;
// 在構(gòu)造函數(shù)體執(zhí)行前,
// 語法糖已經(jīng)設(shè)置了變量 x 和 y。
Point(this.x, this.y);
}
在沒有聲明構(gòu)造函數(shù)的情況下, Dart 會提供一個(gè)默認(rèn)的構(gòu)造函數(shù)。 默認(rèn)構(gòu)造函數(shù)沒有參數(shù)并會調(diào)用父類的無參構(gòu)造函數(shù)。
子類不會繼承父類的構(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ù)意圖:
class Point {
num x, y;
Point(this.x, this.y);
// 命名構(gòu)造函數(shù)
Point.origin() {
x = 0;
y = 0;
}
}
切記,構(gòu)造函數(shù)不能夠被繼承, 這意味著父類的命名構(gòu)造函數(shù)不會被子類繼承。 如果希望使用父類中定義的命名構(gòu)造函數(shù)創(chuàng)建子類, 就必須在子類中實(shí)現(xiàn)該構(gòu)造函數(shù)。
默認(rèn)情況下,子類的構(gòu)造函數(shù)會自動(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í)行順序如下:
如果父類中沒有匿名無參的構(gòu)造函數(shù), 則需要手工調(diào)用父類的其他構(gòu)造函數(shù)。 在當(dāng)前構(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ù)可以是一個(gè)表達(dá)式或者一個(gè)方法調(diào)用:
class Employee extends Person {
Employee() : super.fromJson(getDefaultData());
// ···
}
警告: 調(diào)用父類構(gòu)造函數(shù)的參數(shù)無法訪問 this 。 例如,參數(shù)可以為靜態(tài)函數(shù)但是不能是實(shí)例函數(shù)。
除了調(diào)用超類構(gòu)造函數(shù)之外, 還可以在構(gòu)造函數(shù)體執(zhí)行之前初始化實(shí)例變量。 各參數(shù)的初始化用逗號分隔。
// 在構(gòu)造函數(shù)體執(zhí)行之前,
// 通過初始列表設(shè)置實(shí)例變量。
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
警告: 初始化程序的右側(cè)無法訪問 this 。
在開發(fā)期間, 可以使用 assert 來驗(yàn)證輸入的初始化列表。
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
使用初始化列表可以很方便的設(shè)置 final 字段。 下面示例演示了,如何使用初始化列表初始化設(shè)置三個(gè) 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);
}
有時(shí)構(gòu)造函數(shù)的唯一目的是重定向到同一個(gè)類中的另一個(gè)構(gòu)造函數(shù)。 重定向構(gòu)造函數(shù)的函數(shù)體為空, 構(gòu)造函數(shù)的調(diào)用在冒號 (:) 之后。
class Point {
num x, y;
// 類的主構(gòu)造函數(shù)。
Point(this.x, this.y);
// 指向主構(gòu)造函數(shù)
Point.alongXAxis(num x) : this(x, 0);
}
如果該類生成的對象是固定不變的, 那么就可以把這些對象定義為編譯時(shí)常量。 為此,需要定義一個(gè) const 構(gòu)造函數(shù), 并且聲明所有實(shí)例變量為 final。
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
常量構(gòu)造函數(shù)創(chuàng)建的實(shí)例并不總是常量。 更多內(nèi)容,查看 使用構(gòu)造函數(shù) 章節(jié)。
當(dāng)執(zhí)行構(gòu)造函數(shù)并不總是創(chuàng)建這個(gè)類的一個(gè)新實(shí)例時(shí),則使用 factory 關(guān)鍵字。 例如,一個(gè)工廠構(gòu)造函數(shù)可能會返回一個(gè) cache 中的實(shí)例, 或者可能返回一個(gè)子類的實(shí)例。
以下示例演示了從緩存中返回對象的工廠構(gòu)造函數(shù):
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);
}
}
提示: 工廠構(gòu)造函數(shù)無法訪問 this。
工廠構(gòu)造函的調(diào)用方式與其他構(gòu)造函數(shù)一樣:
var logger = Logger('UI');
logger.log('Button clicked');
更多建議: