Dart官方文档:https://dart.dev/language/constructors
重要说明:本博客基于Dart官网文档,但并不是简单的对官网进行翻译,在覆盖核心功能情况下,我会根据个人研发经验,加入自己的一些扩展问题和场景验证。
如下代码样例,和Java类似,最常用的生成式构造函数:- class Point {
- double x = 0;
- double y = 0;
- Point(double x, double y) {
- this.x = x;
- this.y = y;
- }
- }
复制代码 最佳实战:在Dart中,仅当命名冲突时,才使用this关键字,否则一般可以省略this关键字。
初始化参数列表
如上最常用的构造函数,Dart可以进一步优化如下初始化参数形式。同时,也可以为非空变量设置默认值。- class Point {
- final double x;
- final double y;
- // 在构造函数体执行之前,初始化实例变量
- Point(this.x, this.y);
- }
复制代码 默认构造函数
和Java类似,类如果没有申明构造函数,那么它会有个默认的构造函数。默认构造函数没有入参,它只会调用父类的没有入参的构造函数。
构造函数无法继承
子类无法继承父类的构造函数,如果子类没有申明构造函数,那么这个子类就只有默认构造函数(无论父类是否有其他构造函数)。
命名构造函数
在Dart中,通过命名构造函数,可以为类提供多个构造函数,并且在创建对象时更加清晰。- const double xOrigin = 0;
- const double yOrigin = 0;
- class Point {
- final double x;
- final double y;
- Point(this.x, this.y);
- // `origin`命名构造函数
- Point.origin()
- : x = xOrigin,
- y = yOrigin;
- }
复制代码 特别注意:如上节提到,子类无法继承父类的构造函数,包括父类的命名构造函数。如果子类想使用父类的某个命名构造函数,那么子类必须实现该命名构造函数。
调用父类构造函数
默认情况下,子类无入参的非命名构造函数会调用父类的无入参的非命名构造函数。父类的构造函数在构造函数体之前被调用。如果有初始化参数列表,那么初始化参数列表在父类构造函数调用之前被调用。
构造函数相关的调用顺序如下:
- 初始化参数列表
- 父类无入参的构造函数
- 子类无入参的构造函数
特别注意:如果父类没有通过无入参且非命名的构造函数,那么我们必须手工调用父类的一个构造函数,通过冒号:后面紧跟父类构造函数。
如下代码样例,Person是父类,它仅申明了一个命名构造参数。Employee是继承Person的子类,由于父类没有申明无入参非命名的构造函数,因此在它构造函数都必须手工调用父类的某个构造函数。如命名构造函数fromJson后面,通过冒号:调用了父类的命名构造函数。- // 未申明:无入参、非命名的构造函数
- class Person {
- String? firstName;
- Person.fromJson(Map data) {
- print('in Person');
- }
- }
- class Employee extends Person {
- // 手工调用父类的构造函数:super.fromJson()
- Employee.fromJson(super.data) : super.fromJson() {
- print('in Employee');
- }
- }
- void main() {
- var employee = Employee.fromJson({});
- print(employee);
- // 结果:
- // in Person
- // in Employee
- // Instance of 'Employee'
- }
复制代码 特别注意:由于构造函数参数是在调用构造函数之前计算,因此构造函数的参数可以是表达式,如函数调用等。父类的构造函数不能使用this.关键字,因为参数可以是表达式、静态函数等,并不一定是类实例。- class Employee extends Person {
- Employee() : super.fromJson(fetchDefaultData());
- // ···
- }
复制代码 手工调用父类的构造函数,然后逐一设置入参比较繁琐,如果我们想要简化,那么可以父类的初始值构造函数。这个功能不能与重定向构造函数一起使用(因为语法冲突)。- class Vector2d {
- final double x;
- final double y;
- Vector2d(this.x, this.y);
- }
- class Vector3d extends Vector2d {
- final double z;
- // 默认情况下,我们的使用方法:
- // Vector3d(final double x, final double y, this.z) : super(x, y);
-
- // 简化版本:
- Vector3d(super.x, super.y, this.z);
- }
复制代码 如下代码样例,父类的初始化构造函数可以通过命名参数调用。- class Vector2d {
- // ...
- Vector2d.named({required this.x, required this.y});
- }
- class Vector3d extends Vector2d {
- // ...
- Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
- // 等价调用
- // Vector3d.yzPlane({required double y, required this.z}) : super.named(x: 0, y: y);
- }
复制代码 初始化列表
在构造函数执行之前,我们可以调用父类的构造函数,还可以初始化实例变量。实例变量初始化通过逗号分隔。- Point.fromJson(Map<String, double> json)
- : x = json['x']!,
- y = json['y']! {
- print('In Point.fromJson(): ($x, $y)');
- }
复制代码 开发阶段,我们可以在初始化列表中增加断言:- Point.withAssert(this.x, this.y) : assert(x >= 0) {
- print('In Point.withAssert(): ($x, $y)');
- }
复制代码 初始化列表在设置final不可变量时非常有用:- import 'dart:math';
- class Point {
- final double x;
- final double y;
- final double distanceFromOrigin;
- Point(double x, double y)
- : x = x,
- y = y,
- distanceFromOrigin = sqrt(x * x + y * y);
- }
- void main() {
- var p = Point(2, 3);
- print(p.distanceFromOrigin);
- // 输出:3.605551275463989
- }
复制代码 重定向构造函数
重定向构造函数,就是使用类的其他的构造函数,重定向到的构造函数使用this关键字:- class Point {
- double x, y;
- // 主构造函数
- Point(this.x, this.y);
- // 重定向到主构造函数
- Point.alongXAxis(double x) : this(x, 0);
- }
复制代码 常量构造函数
如果对象的数据不会改变,这些对象可以作为编译期常量。
常量构造函数要求:实例变量都是final不可变量,定义一个const修饰符的构造函数。
特别注意:上一文我们有提到常量构造函数,常量构造函数创建的对象并<b>不一定 |