内容

通过创建一个与它的类同名的函数(另外,还可以选择一个附加标识符,如 命名构造函数 中所述)来声明一个构造函数。

使用最常见的构造函数,即生成构造函数,来创建一个类的实例,并初始化形式参数,以实例化任何实例变量(如果需要的话)。

dart
class Point {
  double x = 0;
  double y = 0;

  // Generative constructor with initializing formal parameters:
  Point(this.x, this.y);
}

this 关键字引用当前实例。

初始化形式参数

#

Dart 具有初始化形式参数,用于简化将构造函数参数分配给实例变量的常见模式。在构造函数声明中直接使用 this.propertyName,并省略主体。

初始化参数还允许你初始化不可为空或 final 实例变量,这两个变量都必须初始化或提供默认值。

dart
class Point {
  final double x;
  final double y;

  // Sets the x and y instance variables
  // before the constructor body runs.
  Point(this.x, this.y);
}

由初始化形式引入的变量隐式为 final,并且仅在 初始化程序列表 的作用域内。

如果你需要执行初始化程序列表中无法表达的某些逻辑,请创建一个 工厂构造函数(或 静态方法)来执行该逻辑,然后将计算出的值传递给一个普通构造函数。

默认构造函数

#

如果你不声明构造函数,则会为你提供一个默认构造函数。默认构造函数没有参数,并且调用超类中的无参数构造函数。

构造函数不会被继承

#

子类不会从其超类中继承构造函数。未声明任何构造函数的子类仅具有默认(无参数、无名称)构造函数。

命名构造函数

#

使用命名构造函数来为一个类实现多个构造函数或提供额外的清晰度。

dart
const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  final double x;
  final double y;

  // Sets the x and y instance variables
  // before the constructor body runs.
  Point(this.x, this.y);

  // Named constructor
  Point.origin()
      : x = xOrigin,
        y = yOrigin;
}

请记住,构造函数不会被继承,这意味着超类的命名构造函数不会被子类继承。如果你希望使用超类中定义的命名构造函数来创建子类,则必须在子类中实现该构造函数。

调用非默认超类构造函数

#

默认情况下,子类中的构造函数会调用超类的未命名、无参数构造函数。超类的构造函数在构造函数主体的开头被调用。如果同时使用 初始化程序列表,则它会在调用超类之前执行。总之,执行顺序如下:

  1. 初始化器列表
  2. 超类的无参构造函数
  3. 主类的无参构造函数

如果超类没有未命名且无参数的构造函数,则必须手动调用超类中的一个构造函数。在冒号 (:) 后指定超类构造函数,就在构造函数体之前(如果有)。

在以下示例中,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().
  Employee.fromJson(super.data) : super.fromJson() {
    print('in Employee');
  }
}

void main() {
  var employee = Employee.fromJson({});
  print(employee);
  // Prints:
  // in Person
  // in Employee
  // Instance of 'Employee'
}

由于在调用构造函数之前会计算超类构造函数的参数,因此参数可以是表达式,例如函数调用

dart
class Employee extends Person {
  Employee() : super.fromJson(fetchDefaultData());
  // ···
}

超参数

#

为了避免必须将每个参数手动传递到构造函数的超调用中,可以使用超初始化器参数将参数转发到指定的或默认超类构造函数。此功能不能与重定向构造函数一起使用。超初始化器参数具有与初始化形式参数类似的语法和语义

dart
class Vector2d {
  final double x;
  final double y;

  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;

  // Forward the x and y parameters to the default super constructor like:
  // Vector3d(final double x, final double y, this.z) : super(x, y);
  Vector3d(super.x, super.y, this.z);
}

如果超构造函数调用已经具有位置参数,则超初始化器参数不能是位置参数,但它们始终可以命名

dart
class Vector2d {
  // ...

  Vector2d.named({required this.x, required this.y});
}

class Vector3d extends Vector2d {
  // ...

  // Forward the y parameter to the named super constructor like:
  // Vector3d.yzPlane({required double y, required this.z})
  //       : super.named(x: 0, y: y);
  Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
}

初始化器列表

#

除了调用超类构造函数之外,您还可以在构造函数体运行之前初始化实例变量。用逗号分隔不同的初始化器。

dart
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, double> json)
    : x = json['x']!,
      y = json['y']! {
  print('In Point.fromJson(): ($x, $y)');
}

在开发过程中,您可以使用初始化器列表中的 assert 来验证输入。

dart
Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

在设置 final 字段时,初始化器列表非常方便。以下示例在初始化器列表中初始化了三个 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);
}

重定向构造函数

#

有时,构造函数的唯一目的是重定向到同一类中的另一个构造函数。重定向构造函数的主体为空,构造函数调用(使用 this 而不是类名)出现在冒号 (:) 之后。

dart
class Point {
  double x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(double x) : this(x, 0);
}

常量构造函数

#

如果您的类生成永不更改的对象,则可以将这些对象设为编译时常量。为此,请定义一个 const 构造函数并确保所有实例变量都是 final

dart
class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

常量构造函数并不总是创建常量。有关详细信息,请参阅有关使用构造函数的部分。

工厂构造函数

#

在实现一个并非总是创建其类的实例的构造函数时,请使用factory关键字。例如,工厂构造函数可能会从缓存中返回一个实例,或者它可能会返回一个子类型的实例。工厂构造函数的另一个用例是使用无法在初始化器列表中处理的逻辑初始化一个 final 变量。

在以下示例中,Logger工厂构造函数从缓存中返回对象,而Logger.fromJson工厂构造函数从 JSON 对象初始化一个 final 变量。

dart
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) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  factory Logger.fromJson(Map<String, Object> json) {
    return Logger(json['name'].toString());
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

像调用任何其他构造函数一样调用工厂构造函数

dart
var logger = Logger('UI');
logger.log('Button clicked');

var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);