前言
Dart 語言的構(gòu)造函數(shù)和其他語言會有些不同鲫尊,我們列舉一下 Dart 中的構(gòu)造函數(shù)的幾種形式笔横。
// 最常見的形式
class Person {
String name;
int age;
Person(this.name, this.age);
}
// 命名構(gòu)造函數(shù)
class Person {
late String name;
late int age;
Person.fromJson(Map<String, dynamic> json) {
name = json['name'];
age = json['age'];
}
}
// 工廠構(gòu)造函數(shù)
class Person {
late String name;
static final Map<String, Person> _cache =
<String, Person>{};
factory Person.withName(String name) {
return _cache.putIfAbsent(
name, () => Person._internal(name));
}
Person._internal(this.name);
}
// 常量構(gòu)造函數(shù)
class Person {
final String name;
final int age;
const Person(this.name, this.age);
}
// 使用其他構(gòu)造函數(shù)構(gòu)造
class Person {
String name;
int age;
Person(this.name, this.age);
Person.anymous(int age): this('Anymous', age);
}
// 帶參數(shù)斷言的構(gòu)造函數(shù)(debug 模式有效)
class Person {
String name;
int age;
Person.withAssert(this.name, this.age)
: assert(age >= 0),
assert(name.length > 0);
}
// 使用父類構(gòu)造函數(shù)構(gòu)建
class Student extends Person {
String school;
Student(name, age, this.school) : super(name, age);
}
這里比較容易混淆的是命名構(gòu)造函數(shù)和工廠構(gòu)造函數(shù)。實際上工廠構(gòu)造函數(shù)的特點是不一定返回新的實例咐吼,比如我們示例的代碼吹缔,可以從緩存中取出已有對象返回。同時锯茄,工廠構(gòu)造函數(shù)還可以返回該類的子類厢塘。而且工廠構(gòu)造函數(shù)是要通過 return
語句返回一個對象,而命名構(gòu)造函數(shù)不允許返回肌幽,只能構(gòu)建當(dāng)前類的對象晚碾。這么多構(gòu)造函數(shù),該如何合理使用喂急,我們來看看官方的指引規(guī)范格嘁。
規(guī)則1:盡可能使用構(gòu)造函數(shù)直接初始化成員的方式
Dart 提供了一種快捷初始化成員屬性的方式,那就是在構(gòu)造函數(shù)中使用 this.
語法廊移,這時候?qū)?yīng)的成員會自動賦值而無需手動進(jìn)行賦值糕簿。這樣的初始化成員的方式更加簡潔探入。
// 正確示例
class Point {
double x, y;
Point(this.x, this.y);
}
// 錯誤示例
class Point {
double x, y;
Point(double x, double y)
: x = x,
y = y;
}
規(guī)則2:如果構(gòu)造函數(shù)會初始化成員,那么不要使用 late
修飾懂诗。
聲明式 null safety 要求非空字段必須在使用之前被初始化蜂嗽。由于成員屬性可能在構(gòu)造函數(shù)中使用,因此如果不初始化非空成員的話殃恒,會導(dǎo)致編譯器報錯植旧。可以通過在成員前加 late 修飾离唐,這時候如果不初始化而直接使用該成員的話 病附,會將編譯時錯誤轉(zhuǎn)換為運行時錯誤。這種情況下最好的解決辦法是為構(gòu)造函數(shù)提供成員清單進(jìn)行初始化侯繁。
// 正確示例
class Point {
double x, y;
Point.polar(double theta, double radius)
: x = cos(theta) * radius,
y = sin(theta) * radius;
}
// 錯誤示例
class Point {
late double x, y;
Point.polar(double theta, double radius) {
x = cos(theta) * radius;
y = sin(theta) * radius;
}
}
使用初始化清單的形式的好處是在使用這些成員之前能夠確保已經(jīng)得到了初始化胖喳,哪怕是構(gòu)造函數(shù)中。我們可以理解為是一種語法糖贮竟,在構(gòu)造函數(shù)最開始處對這些成員進(jìn)行了初始化丽焊。
規(guī)則3:對于空構(gòu)造函數(shù)體,使用分號結(jié)束而不是{}空函數(shù)體
在 Dart 中咕别,如果構(gòu)造函數(shù)體為空技健,那么可以簡單地用分號結(jié)束函數(shù),而無需使用大括號來寫一個空的函數(shù)體惰拱。
// 正確示例
class Point {
double x, y;
Point(this.x, this.y);
}
//錯誤示例
class Point {
double x, y;
Point(this.x, this.y) {}
}
規(guī)則4:不要使用 new 來構(gòu)建新對象
Dart 2版本以后雌贱,new
關(guān)鍵字是可選的。new
關(guān)鍵字實際上在 Dart 中已經(jīng)被標(biāo)記為棄用了偿短。當(dāng)你習(xí)慣了沒有 new
的方式后欣孤,你肯定會和我一樣,覺得加了 new
關(guān)鍵字的代碼很丑陋昔逗,尤其是在組件樹中降传。
// 正確示例
Widget build(BuildContext context) {
return Row(
children: [
RaisedButton(
child: Text('Increment'),
),
Text('Click!'),
],
);
}
// 錯誤示例
Widget build(BuildContext context) {
return new Row(
children: [
new RaisedButton(
child: new Text('Increment'),
),
new Text('Click!'),
],
);
}
規(guī)則5:不要加多余的 const
當(dāng)一個對象在上下文中必須是常量時,const
關(guān)鍵字是隱式的勾怒,而無需再單獨寫婆排。下面列舉的上下文中的表達(dá)式是默認(rèn)就是 const
的:
- 一個不變的集合;
- 調(diào)用聲明為
const
的構(gòu)造函數(shù)笔链; - 元數(shù)據(jù)的注解段只;
- 用于常量聲明時的初始化對象(見下面的示例)
-
switch-case
語句中的case
冒號后面的表達(dá)式。
// 正確示例
const primaryColors = [
Color('red', [255, 0, 0]),
Color('green', [0, 255, 0]),
Color('blue', [0, 0, 255]),
];
// 錯誤示例
const primaryColors = const [
const Color('red', const [255, 0, 0]),
const Color('green', const [0, 255, 0]),
const Color('blue', const [0, 0, 255]),
];
基本上鉴扫,在 Dart 2中赞枕,如果一個對象前面加 new
來替代 const
會報錯的話,那么就隱式是 const
的,此時無需再額外加 const
鹦赎。
總結(jié)
Dart 語言為我們提供了很多簡寫構(gòu)造函數(shù)的語法糖谍椅,雖然按其他語言那種形式編寫也不會有什么問題,但是遵循舊的方式如何能夠體現(xiàn) Dart 這門年輕語言的優(yōu)勢呢古话?因此雏吭,多了解 Dart 語言自身的一些特性,可以讓我們的編碼效率更高陪踩,代碼更整潔杖们。