Dart 中的命名构造函数和常量构造函数

1. 命名构造函数

在 Dart 中,和 Java、C++、C# 等语言不同,我们不能通过重载构造函数(即使用相同的构造函数名来创建不同的构造函数)来创建多个构造函数。然而,Dart 提供了一种解决方法:命名构造函数

命名构造函数可以让你使用不同的构造函数名称来初始化对象。它们与默认构造函数不同,因为你可以给它们提供自己定义的名称,这样就可以根据需要为类创建多个不同的构造函数。

1.1 命名构造函数的基本语法

命名构造函数的语法是在类的构造函数名称后面使用 . 来指定不同的构造函数名称。

dart 复制代码
class Bicycle {
  String? color;
  int? size;
  int? currentSpeed;

  // 默认构造函数
  Bicycle(this.color, this.size, this.currentSpeed);

  // 命名构造函数
  Bicycle.redBike(int size, int speed) {
    color = "Red";  // 默认颜色为 Red
    this.size = size;
    currentSpeed = speed;
  }

  // 另一个命名构造函数
  Bicycle.blueBike(int size) {
    color = "Blue";  // 默认颜色为 Blue
    this.size = size;
    currentSpeed = 0;  // 默认速度为 0
  }

  void display() {
    print("Color: $color");
    print("Size: $size");
    print("Current Speed: $currentSpeed");
  }
}

void main() {
  // 使用默认构造函数创建对象
  var myBicycle = Bicycle("Green", 26, 10);
  myBicycle.display();

  // 使用命名构造函数创建不同的对象
  var redBike = Bicycle.redBike(28, 15); // 使用命名构造函数
  redBike.display(); // 输出: Color: Red, Size: 28, Current Speed: 15

  var blueBike = Bicycle.blueBike(20); // 使用另一个命名构造函数
  blueBike.display(); // 输出: Color: Blue, Size: 20, Current Speed: 0
}

1.2 命名构造函数的用途

命名构造函数的一个常见用途是为类创建多个初始化方式 。通过不同的构造函数名称 ,你可以根据不同的情况来初始化对象 ,从而提供更灵活的对象创建方式

  • 多个构造函数:一个类可以有多个命名构造函数,每个命名构造函数可以有不同的参数和初始化方式。
  • 代码的可读性 :命名构造函数能够让代码更具可读性。当我们看到 Bicycle.redBike()Bicycle.blueBike() 时,我们能够直观地知道这个对象的构造方式和初始化值,而不需要深入查看构造函数的实现。

1.3 命名构造函数与默认构造函数的组合

你可以在一个类中同时使用默认构造函数和命名构造函数。默认构造函数通常用来进行基本的初始化,而命名构造函数可以用于处理更特殊的初始化场景。

dart 复制代码
class Rectangle {
  double width;
  double height;

  // 默认构造函数
  Rectangle(this.width, this.height);

  // 命名构造函数:创建正方形
  Rectangle.square(double side) : width = side, height = side;

  void display() {
    print("Width: $width, Height: $height");
  }
}

void main() {
  // 使用默认构造函数
  var rect1 = Rectangle(10, 20);
  rect1.display(); // 输出: Width: 10.0, Height: 20.0

  // 使用命名构造函数创建正方形
  var square = Rectangle.square(5);
  square.display(); // 输出: Width: 5.0, Height: 5.0
}

在这个例子中,Rectangle 类有两个构造函数:

  1. 默认构造函数 :用于初始化 widthheight
  2. 命名构造函数 Rectangle.square():用于创建正方形,widthheight 会被赋予相同的值。

1.4 命名构造函数与初始化列表

你还可以在命名构造函数中使用初始化列表来初始化字段 。这允许你在构造函数体执行之前对字段进行初始化。

dart 复制代码
class Circle {
  double radius;

  // 默认构造函数
  Circle(this.radius);

  // 命名构造函数:根据面积创建圆
  Circle.fromArea(double area) : radius = (area / 3.14159).sqrt();

  void display() {
    print("Radius: $radius");
  }
}

void main() {
  var circle1 = Circle(10);  // 使用默认构造函数
  circle1.display(); // 输出: Radius: 10.0

  var circle2 = Circle.fromArea(314.159);  // 使用命名构造函数
  circle2.display(); // 输出: Radius: 10.0
}

在这个例子中,命名构造函数 Circle.fromArea() 使用初始化列表计算出圆的半径,并根据面积来创建对象。

1.5 总结

命名构造函数是 Dart 中处理多个构造函数的一种方法。它们允许你为类创建多个不同的构造函数 ,提供灵活的对象创建方式 ,增强代码的可读性和可维护性。命名构造函数可以与默认构造函数一起使用,也可以使用初始化列表来进一步简化代码。

命名构造函数的优点包括:

  • 允许同一类拥有多个不同的构造函数,适应不同的初始化需求
  • 提高代码的清晰度和可读性,特别是当需要明确标识不同初始化方式时。

通过命名构造函数,你可以更加灵活地控制对象的创建过程,使你的代码更加直观和易于理解。

2. 常量构造函数(const 构造函数)

在 Dart 中,常量构造函数const 构造函数)允许你创建编译时常量的对象。这些对象在编译时就被完全初始化,且在程序运行期间是不可变的。常量对象通常用于提高程序的性能和减少内存占用,因为常量对象只会在内存中创建一次并被重用。

常量构造函数是通过在构造函数前加上 const 关键字来定义的。

2.1 常量构造函数的语法

常量构造函数的定义与普通构造函数相似,区别在于构造函数前加上了 const 关键字。

dart 复制代码
class Point {
  final int x;
  final int y;

  // 常量构造函数
  const Point(this.x, this.y);

  void display() {
    print("Point: ($x, $y)");
  }
}

void main() {
  // 创建常量对象
  const point1 = Point(2, 3); // 使用常量构造函数创建常量对象
  const point2 = Point(2, 3); // 使用常量构造函数创建相同的对象
  
  print(identical(point1, point2)); // 输出: true,point1 和 point2 是同一个对象
}

在上面的代码中,Point 类有一个常量构造函数 const Point(this.x, this.y)。通过 const 关键字创建的对象 point1point2 是编译时常量,它们在内存中共享同一个实例,因此 identical(point1, point2) 返回 true

2.2 常量构造函数的特性

  • 编译时常量 :使用常量构造函数创建的对象是编译时常量,意味着它们的值在程序编译时就已经确定。常量对象在运行时不能修改 ,并且在程序中只会存在一个实例。

  • 常量池:Dart 会自动将常量对象缓存到一个常量池中,这意味着当多个相同的常量对象被创建时,Dart 会重用已经创建的对象实例,而不会创建新的对象实例。

  • 不可变性 :常量对象的所有字段必须是 finalconst,且对象创建后不可再修改。这保证了常量对象的不可变性。

2.3 常量构造函数的使用场景

常量构造函数通常用于以下场景:

  • 不变的对象 :当你知道某些对象的状态在创建后不会变化时,可以使用常量构造函数。这样,Dart 会在内存中缓存这些对象,从而节省内存和提高性能。

  • 性能优化 :常量对象只会在内存中创建一次,因此在需要频繁访问相同的对象时,常量对象能够避免重复创建,提升性能。

  • 常量集合:在集合中使用常量对象时(如列表、映射等),它们的值在整个程序中是唯一的,避免了重复存储相同数据。

示例:

dart 复制代码
class Color {
  final int red, green, blue;

  // 常量构造函数
  const Color(this.red, this.green, this.blue);

  void display() {
    print("Color: ($red, $green, $blue)");
  }
}

void main() {
  const red = Color(255, 0, 0);  // 使用常量构造函数创建红色对象
  const green = Color(0, 255, 0);  // 使用常量构造函数创建绿色对象

  // 创建相同的颜色对象时,Dart 会重用相同的实例
  const redAgain = Color(255, 0, 0);

  print(identical(red, redAgain)); // 输出: true,red 和 redAgain 是同一个对象
}

在这个例子中,Color 类的常量构造函数通过 const 关键字定义。每次创建 Color(255, 0, 0) 时,Dart 都会检查常量池中是否已有该对象。如果有,Dart 会重用已有的对象实例,从而节省内存。

2.4 常量构造函数与 final 关键字的关系

在常量构造函数中,所有的字段必须是 final 类型,因为常量对象的状态在创建后不可改变。final 表示一旦赋值后,字段值不能被修改。

dart 复制代码
class Circle {
  final double radius;

  // 常量构造函数
  const Circle(this.radius);

  void display() {
    print("Circle radius: $radius");
  }
}

void main() {
  const circle1 = Circle(5.0);  // 创建常量对象
  const circle2 = Circle(5.0);  // 创建相同的常量对象

  print(identical(circle1, circle2)); // 输出: true
}

2.5 常量构造函数的限制

  • 必须是常量表达式 :常量构造函数只能接受常量表达式作为参数。也就是说,传给构造函数的参数必须是已知的常量,不能是运行时计算的结果
dart 复制代码
class Square {
  final int sideLength;

  // 常量构造函数
  const Square(this.sideLength);

  void display() {
    print("Square side length: $sideLength");
  }
}

void main() {
  const square1 = Square(5);  // 正常:传递常量值
  const square2 = Square(5 * 2);  // 正常:常量表达式(编译时可以计算)

  // 以下代码是错误的,因为 `sideLength` 不能是运行时值
  // var length = 10;
  // const square3 = Square(length); // 错误!`length` 不是常量表达式
}

在这个例子中,Square 的常量构造函数只能接受编译时常量(例如 55 * 2)。如果你尝试传递一个运行时才确定的值(如变量 length),编译会报错。

2.6 总结

  • 常量构造函数const)允许你创建不可变且在内存中共享的对象,这些对象在程序编译时就已经确定
  • 常量构造函数的对象被缓存,多个相同的常量对象会共享一个实例,从而节省内存和提高性能。
  • 常量构造函数的参数必须是常量表达式,且对象的字段必须是 final
  • 常量构造函数通常用于创建不变的、共享的对象,或者用于需要高效内存使用和性能优化的场景。

通过常量构造函数,你可以有效地管理和优化不可变对象,确保它们在整个应用中只有一个实例,并减少不必要的内存开销。

3. 总结

特性 命名构造函数 常量构造函数
定义 通过类名后跟 . 来创建 通过在构造函数前加 const 关键字来定义
可变性 对象可以是可变的 对象必须是不可变的
参数 可以是运行时计算的值 参数必须是编译时常量
用途 提供多种初始化方式 创建可共享的、不可变的对象
相关推荐
VUE4 小时前
Dart 中的封装 继承 多态
dart
iFlyCai3 天前
深入理解Flutter生命周期函数之StatefulWidget(一)
flutter·生命周期·dart·statefulwidget
有趣的杰克10 天前
Flutter【04】高性能表单架构设计
android·flutter·dart
iFlyCai11 天前
23种设计模式的Flutter实现第一篇创建型模式(一)
flutter·设计模式·dart
hello world smile12 天前
Dart中List API用法大全
flutter·list·dart
iFlyCai15 天前
Flutter 中 Provider 的使用指南
flutter·dart
hello world smile17 天前
最全的Flutter中pubspec.yaml及其yaml 语法的使用说明
android·前端·javascript·flutter·dart·yaml·pubspec.yaml
iFlyCai23 天前
使用GetX实现GetPage中间件
flutter·中间件·dart
CodeOfCC1 个月前
Flutter 将ffmpeg动态库制做成插件
flutter·ffmpeg·dart·1024程序员节