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 关键字来定义
可变性 对象可以是可变的 对象必须是不可变的
参数 可以是运行时计算的值 参数必须是编译时常量
用途 提供多种初始化方式 创建可共享的、不可变的对象
相关推荐
迷雾漫步者1 天前
Flutter组件————PageView
flutter·跨平台·dart
迷雾漫步者1 天前
Flutter组件————FloatingActionButton
前端·flutter·dart
捡芝麻丢西瓜2 天前
flutter自学笔记5- dart 编码规范
flutter·dart
迷雾漫步者4 天前
Flutter组件————AppBar
flutter·跨平台·dart
迷雾漫步者4 天前
Flutter组件————Scaffold
flutter·dart
叫我菜菜就好11 天前
【Flutter_Web】Flutter编译Web第一篇(插件篇):Flutter_web实现上传TOS上传资源,编写web插件
前端·javascript·flutter·dart
我码玄黄16 天前
Flutter响应式设计
flutter·响应式设计·dart
VUE1 个月前
Dart 中的封装 继承 多态
dart
iFlyCai1 个月前
深入理解Flutter生命周期函数之StatefulWidget(一)
flutter·生命周期·dart·statefulwidget