介紹
因?yàn)镕lutter和Dart語言非常容易上手,很多學(xué)習(xí)Flutter的同學(xué),一般都會(huì)直接寫Flutter頁面,對(duì)Dart語言也是一知半解,包括我自己。
Dart語言和很多現(xiàn)代語言都很類似,但是其構(gòu)造函數(shù),其靈活性,還是比我了解的其它語言要多的,所以在寫構(gòu)造函數(shù)的時(shí)候,要么莫名其妙報(bào)錯(cuò),要么寫的很復(fù)雜,沒有將其精髓掌握。
下面跟大家總結(jié)一下Dart構(gòu)造函數(shù)的語法,方便大家查閱
語法介紹
格式
Dart構(gòu)造函數(shù)有4種格式:
ClassName(...) //普通構(gòu)造函數(shù)Classname.identifier(...) //命名構(gòu)造函數(shù)const ClassName(...) //常量構(gòu)造函數(shù)factroy ClassName(...) //工廠構(gòu)造函數(shù)
使用
在定義自己的構(gòu)造函數(shù)之前,我們會(huì)接觸到構(gòu)造函數(shù)的使用,其用法如下:
var p1 = Point(2, 2); //Dart2中,可以省略構(gòu)造函數(shù)前的new
var p2 = Point.fromJson({'x': 1, 'y': 2});
var p = const ImmutablePoint(2, 2); //常量構(gòu)造函數(shù),用來創(chuàng)建編譯期常量
插播廣告
如果想知道某個(gè)變量屬于哪個(gè)類,可以使用runtimeType:
print('The type of a is ${a.runtimeType}');
定義
1. 默認(rèn)構(gòu)造函數(shù)
如果你定義了一個(gè)類,而沒有定義構(gòu)造函數(shù),那么它將有一個(gè)默認(rèn)的構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù) 沒有參數(shù)
如果這個(gè)類有父類,那么默認(rèn)構(gòu)造函數(shù),還會(huì)調(diào)用父類的無參數(shù)構(gòu)造函數(shù)。
2. 普通構(gòu)造函數(shù)
這就是我們普通的構(gòu)造函數(shù),其樣子和其它語言幾乎一樣
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;
}
}
上例中只有兩個(gè)成員變量,如果有10個(gè),豈不是麻煩死?所以Dart有語法糖給你哦:
class Point {
num x, y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
它可以將x,y的賦值變得簡單一些,就不用寫構(gòu)造函數(shù)的方法體了,記得括號(hào)后用分號(hào)哦。
3. 命名構(gòu)造函數(shù)
class Point {
num x, y;
Point(this.x, this.y);
// 命名構(gòu)造函數(shù),新增代碼
Point.origin() {
x = 0;
y = 0;
}
}
請(qǐng)記住,命名構(gòu)造函數(shù)不可繼承,如果子類想要有 和父類一樣的命名構(gòu)造函數(shù),那就寫個(gè)同名的(通常也會(huì)在子類的命名構(gòu)造函數(shù)里,調(diào)用父類的同名命名構(gòu)造函數(shù))
4. 構(gòu)造函數(shù)調(diào)用父類構(gòu)造函數(shù)的順序
如果你的類,繼承于父類,那么子類的構(gòu)造函數(shù),勢必要調(diào)用父類的構(gòu)造函數(shù),這時(shí)候就要分兩種情況:
- Dart語言幫你調(diào)用父類的無參數(shù)構(gòu)造函數(shù)
- 代碼中顯式調(diào)用父類的構(gòu)造函數(shù)
4.1 默認(rèn)調(diào)用調(diào)用父類的無參數(shù)構(gòu)造函數(shù)
如果你沒有顯式調(diào)用父類的構(gòu)造函數(shù),并且父類有一個(gè)無參數(shù)構(gòu)造函數(shù),那么Dart就會(huì)幫你在子類的構(gòu)造函數(shù)方法體的最前面,調(diào)用父類的無參數(shù)構(gòu)造函數(shù)。當(dāng)然,后面我們會(huì)說道,構(gòu)造函數(shù)分成好幾部分來初始化成員變量,調(diào)用的順序如下:
- 初始化列表
- 父類的無參數(shù)構(gòu)造函數(shù)
- 子類的無參數(shù)構(gòu)造函數(shù)
當(dāng)然,如果父類沒有無參數(shù)構(gòu)造函數(shù),或者Dart這種隱式調(diào)用無法滿足你的要求,那就需要顯式調(diào)用父類的構(gòu)造函數(shù)了
4.2 顯式調(diào)用父類構(gòu)造函數(shù)
顯式調(diào)用父類構(gòu)造函數(shù),應(yīng)該在初始化列表中完成(記得好像在C++中見到過初始化列表?太久了忘記了)
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
}
初始化列表就是構(gòu)造函數(shù)名的冒號(hào)后面,打括號(hào)前面的部分。
5. 初始化列表
初始化列表的執(zhí)行順序,在整個(gè)構(gòu)造函數(shù)的最前面,它除了可以調(diào)用父類的構(gòu)造函數(shù),還可以在構(gòu)造函數(shù)方法體之前,初始化一些成員變量。
// 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)');
}
尤其是初始化那些final修飾的成員變量時(shí),初始化列表很有用,因?yàn)樵诜椒w中,不能給final修飾的成員變量賦值,因?yàn)?strong>在執(zhí)行方法體的時(shí)候,final修飾的成員變量已經(jīng)不能變了。這個(gè)地方很多人犯錯(cuò)。
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);
}
6. 構(gòu)造函數(shù)傳遞
定義構(gòu)造函數(shù)的時(shí)候,除了一個(gè)普通構(gòu)造函數(shù),還可以有若干命名構(gòu)造函數(shù),這些構(gòu)造函數(shù)之間,有時(shí)候會(huì)有一些相同的邏輯,如果分別書寫在各個(gè)構(gòu)造函數(shù)中,會(huì)有些多余,所以構(gòu)造函數(shù)可以傳遞。
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);
}
傳遞構(gòu)造函數(shù),沒有方法體,會(huì)在初始化列表中,調(diào)用另一個(gè)構(gòu)造函數(shù)。
7. 常量構(gòu)造函數(shù)
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
如果你的類,創(chuàng)建的對(duì)象永遠(yuǎn)不會(huì)改變,你可以在編譯期就創(chuàng)建這個(gè)常量實(shí)例,并且定義一個(gè)常量構(gòu)造函數(shù),并且確保所有的成員變量都是final的。
8. 工廠構(gòu)造函數(shù)
有時(shí)候可能有一種需求,并不需要每次都創(chuàng)建新的類實(shí)例,而是每一種情況,只需要一個(gè)實(shí)例:
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);
}
}
main() {
var logger = Logger('UI');
logger.log('Button clicked');
}
工廠構(gòu)造函數(shù),沒有權(quán)利訪問this
上例的意思是,類中又一個(gè)靜態(tài)緩存_cache保存著一些Logger類實(shí)例,創(chuàng)建實(shí)例時(shí),給工廠構(gòu)造函數(shù)傳遞的name,如果在緩存中已經(jīng)存在,就用緩存中現(xiàn)成的實(shí)例,如果沒有,就新建一個(gè)實(shí)例,并且也放到緩存中。
如此這般,我們可以創(chuàng)建名字為UI / SYS / API 等的實(shí)例,然后在debug的時(shí)候,如果設(shè)置名字為UI的Logger實(shí)例的mute為true,就不會(huì)打印UI相關(guān)的log,而不影響其它兩個(gè)名字的log。是不是很方便呢?