构造函数

什么是构造函数

构造函数是用于创建类实例的特殊函数,通常用来初始化属性,除默认构造函数外,所有构造函数名称与类名一致。

Dart 构造函数分类:

  1. 生成构造函数(Generative)
  2. 默认构造函数(Default)
  3. 命名构造函数(Named)
  4. 常量构造函数(Const)
  5. 重定向构造函数(Redirecting)
  6. 工厂构造函数(Factory)
  7. 重定向工厂构造函数
  8. 简洁构造语法(Dart 3.13+)
  9. 主构造函数(Primary Constructors,极简字段 + 构造)

1. 生成构造函数 Generative Constructor

最基础构造,创建实例并初始化成员变量,支持初始化形式参数 this.变量 简化赋值。

class Point {
  double x;
  double y;

  // 生成构造函数,this.x 直接接收参数赋值成员
  Point(this.x, this.y); //直接给x y赋值了
}

void main() {
  final p = Point(10, 20);
  print("(${p.x}, ${p.y})"); // (10, 20)
}
Code language: JavaScript (javascript)

2. 默认构造函数 Default Constructor

若类未手动定义任何构造,Dart 自动提供无参、无名称的生成构造函数。和其他类C语言一样 C++ JAVA c#等等都是如此.

class PointA {
  double x = 0;
  double y = 0;
  // 隐式默认构造:PointA();
}

void main() {
  final p = PointA(); // 自动调用默认构造
  print("(${p.x}, ${p.y})"); // (0.0, 0.0)
}
Code language: PHP (php)

注意:只要手动写了任意构造,默认构造就会消失。

3. 命名构造函数 Named Constructor

作用:一个类定义多个构造、语义清晰;格式 类名.构造名()

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

class Point {
  final double x;
  final double y;

  // 主生成构造
  Point(this.x, this.y);

  // 命名构造:原点
  Point.origin() : x = xOrigin, y = yOrigin;
}

void main() {
  final p1 = Point(5, 6);
  final p2 = Point.origin(); //调用命名构造函数
  print(p1.x); //5
  print("原点:(${p2.x}, ${p2.y})"); // (0,0)
}
Code language: PHP (php)

关键规则

  • 子类不会继承父类命名构造,子类需要同名命名构造必须手动实现;
  • 命名构造支持初始化列表赋值 final 变量。

4. 常量构造函数 Const Constructor

用于创建编译期常量对象,要求:

  1. 所有成员变量标记 final
  2. 构造函数加 const 调用时用 const 可复用常量缓存,不写 const 会创建普通实例。

class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0); //静态的属于类的,所有类共用一个
  final double x, y;

  // 常量构造 可以为final的x和y初始化
  const ImmutablePoint(this.x, this.y);
}

void main() {
  // 编译期常量,复用同一个对象
  const p1 = ImmutablePoint(1, 2);
  const p2 = ImmutablePoint(1, 2); //传入的值需要一样,都是1 2 如果不应用 则会不同一个对象
  print(p1 == p2); // true

  // 普通实例,新对象
  final p3 = ImmutablePoint(1, 2);
  print(p1 == p3); // false
}Code language: PHP (php)

5. 重定向构造函数 Redirecting Constructor

构造函数内部转发到同类另一个构造,使用 : this(参数)函数体必须为空

class Point {
  double x, y;

  // 主构造
  Point(this.x, this.y);

  // 重定向构造:只传x,y固定0,转发主构造
  Point.alongX(double x) : this(x, 0); //调用命名构造函数后再把x传给主够在函数,同时y的值赋0
}

void main() {
  final p = Point.alongX(9);
  print("x=${p.x}, y=${p.y}"); // x=9, y=0
}
Code language: JavaScript (javascript)

6. 工厂构造函数 Factory Constructor

关键字 factory,适用场景:

  1. 不总是新建对象(缓存复用、返回子类实例)
  2. 构造前复杂逻辑(解析 JSON、参数校验) 限制:不能访问 this
缓存单例 Logger
class Logger {
  final String name;
  bool mute = false;
  // 私有缓存库内可见
  static final Map<String, Logger> _cache = {};

  // 工厂构造:优先取缓存
  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  // 工厂构造:从json解析创建
  factory Logger.fromJson(Map<String, dynamic> json) {
    return Logger(json["name"].toString());
  }

  // 私有内部生成构造
  Logger._internal(this.name);

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

void main() {
  final log1 = Logger("UI");
  final log2 = Logger("UI");
  print(log1 == log2); // true 同一缓存对象

  final jsonLog = Logger.fromJson({"name": "Net"});
  jsonLog.log("request success");
}
Code language: JavaScript (javascript)

7. 重定向工厂构造函数 Redirecting Factory

格式 factory 名称() = 其他类构造;,适合抽象类代理实例创建,省去重复参数定义。

abstract class Listenable {}

class _MergingListenable implements Listenable {
  List<_MergingListenable> list;
  _MergingListenable(this.list);
}

// 重定向工厂,调用内部私有类构造
factory Listenable.merge(List<Listenable> list) = _MergingListenable;
Code language: PHP (php)

8. 构造函数撕裂 Constructor Tear-offs

直接把构造函数当作函数参数传递,省略 (),替代 lambda 匿名函数,性能更好。

推荐写法
void main() {
  final charCodes = [65, 66, 67];
  // 命名构造 String.fromCharCode
  final strs = charCodes.map(String.fromCharCode);
  print(strs); // (A, B, C)

  // 无名称构造撕裂 .new
  final buffers = charCodes.map(StringBuffer.new);
}
Code language: PHP (php)
不推荐冗余 lambda
// 多余包裹,不推荐
charCodes.map((code) => String.fromCharCode(code));
Code language: JavaScript (javascript)

9. Dart 3.13+ 简洁构造语法 Concise Constructor

类内可省略类名,直接使用 new / factory 定义构造,无需类名.xxx:

传统写法简写
Point(this.x,this.y)new(this.x,this.y)
Point.origin():x=0,y=0new origin():x=0,y=0
factory Point.clone()factory clone()
class Point {
  double x, y;

  // 简写无参生成构造
  new(this.x, this.y);

  // 简写命名生成构造
  new origin() : x = 0, y = 0;

  // 简写工厂构造
  factory clone(Point other) => new(other.x, other.y);
}

void main() {
  final p = new(3, 4);
  final origin = new origin();
  final copy = p.clone(p);
}
Code language: PHP (php)

10. 实例变量初始化三种方式

1:声明时直接赋值
class PointA {
  double x = 1.0;
  double y = 2.0;
}
Code language: JavaScript (javascript)

2:初始化形式参数

this.变量

支持可选参数、命名参数、nullable 变量

class PointB {
  final double x;
  final double y;

  // 位置可选参数
  PointB.opt([this.x = 0, this.y = 0]);

  // 命名参数带默认值
  PointB.named({this.x = 5, this.y = 5});
}
Code language: JavaScript (javascript)

3:初始化列表 Initializer List

冒号 : 后赋值,执行时机早于构造函数体,不能访问 this,支持 assert 参数校验

class Point {
  final double x;
  final double y;

  Point.fromJson(Map<String, double> json)
      : x = json["x"]!,
        y = json["y"]! {
    print("构造体执行,x=$x");
  }

  // 带断言校验
  Point.withAssert(this.x, this.y) : assert(x >= 0);
}
Code language: JavaScript (javascript)

11. 私有字段直接初始化参数(Dart3.12+)

带下划线私有变量,构造参数写 this._字段,外部调用直接使用无下划线公开名。

class Point {
  final double _x;
  final double _y;

  // 内部 _x,外部调用写 x:10
  Point({required this._x, this._y = 0});
}

void main() {
  final p = Point(x: 10); // 不传 _x,直接 x
  print(p._x); // 10
}
Code language: PHP (php)

初始化列表中使用私有变量原名:

class Point {
  final double _x;
  Point({required this._x}) : assert(_x > 0);
}
Code language: JavaScript (javascript)

12. 父类构造与构造继承规则

  1. 子类不继承任何父类构造
  2. 构造执行顺序:初始化列表 → 父类构造 → 子类构造体;
  3. 父类若无无参默认构造,子类必须手动 super() 指定父类构造。
基础 super 调用示例

和java类似,使用super来表示基类,C#中使用base。

class Person {
  final String name;
  Person(this.name);
  Person.fromMap(Map map) : name = map["name"];
}

class Employee extends Person {
  int id;
  // 调用父类命名构造
  Employee(Map data) : super.fromMap(data), id = data["id"];
}
Code language: JavaScript (javascript)

注意:super 参数表达式不能使用 this

Super 参数(Dart2.17+,简化父类传参)

super.变量 直接转发参数给父类,无需手动 super(x,y)

class Vector2d {
  final double x, y;
  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;
  // super.x super.y 自动转发父类构造
  Vector3d(super.x, super.y, this.z);
}

// 父类命名构造 + super 命名参数
class Vector3dYZ extends Vector2d {
  final double z;
  Vector3dYZ({required super.y, required this.z}) : super.named(x: 0);
}
Code language: JavaScript (javascript)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注