[TOC]
通過創建一個與類同名的函數來聲明構造函數(另外,還可以像[命名構造函數]中描述的一樣選擇一個附加標識符)。構造函數最常見的應用形式是使用構造函數生成一個類的新實例:
~~~
class Point {
num x, y;
Point(num x, num y) {
// There's a better way to do this, stay tuned.
this.x = x;
this.y = y;
}
}
~~~
this關鍵字是指當前實例。
>注意:只有在名稱沖突時才使用它。否則,Dart的代碼風格需要省略this
>
使用構造函數的參數為實例復制的使用非常常見,Dart具有語法上的優勢,使這種使用更容易實現:
~~~
class Point {
num x, y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
~~~
## 默認構造函數
如果不聲明構造函數,則為您提供默認構造函數。默認構造函數沒有參數,并在超類中調用無參數構造函數。
## 構造函數不是繼承
子類不從父類繼承構造函數。沒有聲明構造函數的子類只有默認的構造函數(沒有參數,沒有名稱)而不是從父類繼承的構造函數。
## 命名的構造函數
使用命名構造函數可以在一個類中定義多個構造函數,或者讓一個類的作用對于開發人員來說更清晰:
~~~
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
~~~
一定要記住構造函數是不會從父類繼承的,這意味著父類的命名構造函數子類也不會繼承。如果你希望使用在超類中定義的命名構造函數來創建子類,則必須在子類中實現該構造函數。
## 調用非默認的超類構造函數
默認情況下,子類中的構造函數調用父類的未命名的無參數構造函數。父類的構造函數在構造函數體的開始處被調用。如果類中有使用初始化列表,初始化列表將在調用超類之前執行。綜上所述,執行順序如下:
* 初始化列表
* 超類中的無參數構造函數
* main類中的無參數構造函數
如果超類沒有未命名的無參數構造函數,則必須手動調用超類中的一個構造函數。在冒號(:)之后,在構造函數體(如果有的話)之前指定超類構造函數。
在下例中,Employee類的構造函數中調用了他的超類Person中的命名構造函數。
~~~
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';
}
///結果輸出為
in Person
in Employee
~~~
因為父類構造函數的參數是在調用構造函數之前執行的,所以參數可以是表達式,比如函數調用:
~~~
class Employee extends Person {
Employee() : super.fromJson(getDefaultData());
// ···
}
~~~
>警告:在超類的構造函數的參數中不能使用this關鍵字。例如,參數可以調用static方法但是不能調用實例方法
>
## 初始化列表
除了調用超類構造函數之外,還可以在構造函數主體運行之前初始化實例變量。初始值設定項用逗號分開。
~~~
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
~~~
>警告:初始化器的右邊部分中無法訪問this關鍵字。
>
在開發期間,可以通過在初始化列表中使用assert來驗證輸入。
~~~
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
~~~
初始化列表在設置final字段時很方便。下面的示例初始化初始化列表中的三個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);
}
///運行結果
3.605551275463989
~~~
## 重定向構造函數
有時,構造函數的唯一目的是重定向到同一個類中的另一個構造函數。重定向構造函數的主體為空,構造函數調用出現在冒號(:)之后。
~~~
class Point {
num x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
~~~
## 常量構造函數
如果您的類生成的對象不會改變,您可以使這些對象成為編譯時常量。為此,定義一個const構造函數,并確保所有實例變量都是final的。
~~~
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
~~~
## 工廠構造函數
在實現構造函數時使用factory關鍵字,該構造函數并不總是創建類的新實例。例如,工廠構造函數可以從緩存返回實例,也可以返回子類型的實例。
以下示例演示工廠構造函數從緩存返回對象:
~~~
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 = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
~~~
>注意:工廠構造函數不能訪問this關鍵字。
>
調用工廠構造函數,就像調用其他構造函數一樣:
~~~
var logger = Logger('UI');
logger.log('Button clicked');
~~~