最常见的构造函数形式,即生成构造函数,创建一个类的新实例:
 1class Point {
 2  num x, y; //Dart中int和double是num的子类
 3
 4  //this引用当前类对象
 5  Point(num x, num y) {
 6    this.x = x;
 7    this.y = y;
 8  }
 9}

使用语法糖的时候可以这样定义:

 1class Point {
 2  num x, y;
 3
 4  //用于设置x和y的语法糖
 5  //在构造函数体运行之前
 6  Point(this.x, this.y);
 7
 8  @override
 9  String toString() {
10    return ("x: $x, y: $y");
11  }
12}
13
14//print(new Point(100, 200)); -> 打印 x: 100, y: 200
默认构造函数

如果未声明构造函数,则会提供默认构造函数。 默认构造函数没有参数,并在调用父类无参数构造函数。

 1class Parent{
 2  Parent(){
 3    print('In Parent\'s constructor.');
 4  }
 5}
 6
 7class Child extends Parent{
 8  Child(){
 9    print('In Child\'s constructor.');
10  }
11}
12
13//new Child(); -> 打印 In Parent's constructor.  In Child's constructor.
构造函数不能继承

子类不能继承其父类构造函数,一个没有声明构造函数的子类只有默认(无参数,无名称)构造函数

命名构造函数

当我们需要定义一个有特别含义的构造函数的时候,可以通过构造函数.XXX来命名构造函数

 1class Point{
 2  num x;
 3  num y;
 4
 5  Point(this.x, this.y);
 6  
 7  //创建一个坐标原点类
 8  Point.origin(){
 9    this.x = 0;
10    this.y = 0;
11  }
12
13 //创建一个坐标为(100, 100)的类
14 Point.coordinate100(){
15    this.x = 100;
16    this.y = 100;
17  }
18  @override
19  String toString() {
20    return ("x: $x, y: $y");
21  }
22}
调用父类构造函数

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

  • 初始化列表
  • 父类的无参数构造函数
  • 子类的无参数构造函数

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

例1:默认先调用父类的无参数构造函数

 1class Parent{
 2  Parent(){
 3    print('In Parent\'s constructor.');
 4  }
 5}
 6
 7
 8class Child extends Parent{
 9  Child(num x, num y) {
10    print('In Child\'s constructor.');
11  }
12}
13
14//new Child(100, 100); -> 打印
15//In Parent's constructor.
16//In Child's constructor.

例2:手动调用父类其他构造函数

 1class Parent{
 2  num x;
 3  num y;
 4  Parent(this.x, this.y){
 5    print('In Parent\'s constructor.');
 6  }
 7}
 8
 9class Child extends Parent{
10  Child(num x, num y) : super(x, y){
11    print('In Child\'s constructor.');
12  }
13}
14
15//new Child(100, 100); -> 打印
16//In Parent's constructor.
17//In Child's constructor.

关于初始化列表:
调用父类构造函数之前,可以在构造函数体执行之前初始化示例变量,用逗号分隔,该过程叫初始化列表。

 1class Parent{
 2  num x;
 3  num y;
 4  num sum;
 5
 6  Parent(num px, num py) : x = px, y = py, sum = px + py{
 7    print("x: $x, y: $y, sum: $sum");
 8    print('In Parent\'s constructor.');
 9  }
10}
11
12class Child extends Parent{
13  Child(num x, num y) : super(x, y){
14    print('In Child\'s constructor.');
15  }
16}
17
18// new Child(100, 100); -> 打印
19//x: 100, y: 100, sum: 200
20//In Parent's constructor.
21//In Child's constructor.
重定向构造函数

有时构造函数需要重定向到同一个类中的另一个构造函数,在冒号后面用this:

 1class Point {
 2  num x, y;
 3
 4  //类的主构造函数
 5  Point(this.x, this.y);
 6
 7  //重定向到主构造函数
 8  Point.alongXAxis(num x) : this(x, 0);
 9}
常量构造函数

如果你的类需要成为永远不会更改的对象,则可以使这些对象成为编译时常量。 定义const构造函数要确保所有实例变量都是final。

 1class Point {
 2  final num x;
 3  final num y;
 4  static final Point2 origin = const Point2(0, 0);
 5
 6  //const关键字放在构造函数名称之前,且不能有函数体
 7  const Point2(this.x, this.y);
 8}

没有定义final时提示:

使用函数体时提示:

工厂构造函数

在实现不用总是创建新的类对象的时候,可以使用factory关键字。 例如,工厂构造函数可能从缓存中返回实例,或者它可能返回子类型的实例:

 1class Logger {
 2  final String name;
 3  bool mute = false;
 4
 5  //工厂构造函数无法访问this,所以这里要用static
 6  //维护一个缓存的Map对象,
 7  static final Map<String, Logger> _cache =
 8      <String, Logger>{};
 9  
10 //调用该构造函数的时候,会从Map中取出对象
11  factory Logger(String name) {
12    if (_cache.containsKey(name)) {
13      return _cache[name];
14    } else {
15      final logger = Logger._internal(name);
16      _cache[name] = logger;
17      return logger;
18    }
19  }
20
21  Logger._internal(this.name);
22
23  void log(String msg) {
24    if (!mute) print(msg);
25  }
26}
27
28//调用
29var logger = Logger('UI');
30logger.log('Button clicked');
个人笔记记录 2021 ~ 2025