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
类有两个构造函数:
- 默认构造函数 :用于初始化
width
和height
。 - 命名构造函数
Rectangle.square()
:用于创建正方形,width
和height
会被赋予相同的值。
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
关键字创建的对象 point1
和 point2
是编译时常量,它们在内存中共享同一个实例,因此 identical(point1, point2)
返回 true
。
2.2 常量构造函数的特性
-
编译时常量 :使用常量构造函数创建的对象是
编译时常量
,意味着它们的值在程序编译时就已经确定
。常量对象在运行时不能修改 ,并且在程序中只会存在一个实例。 -
常量池:Dart 会自动将常量对象缓存到一个常量池中,这意味着当多个相同的常量对象被创建时,Dart 会重用已经创建的对象实例,而不会创建新的对象实例。
-
不可变性 :常量对象的所有字段必须是
final
或const
,且对象创建后不可再修改。这保证了常量对象的不可变性。
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
的常量构造函数只能接受编译时常量(例如 5
或 5 * 2
)。如果你尝试传递一个运行时才确定的值(如变量 length
),编译会报错。
2.6 总结
- 常量构造函数 (
const
)允许你创建不可变且在内存中共享
的对象,这些对象在程序编译时就已经确定
。 - 常量构造函数的对象被缓存,多个相同的常量对象会共享一个实例,从而节省内存和提高性能。
- 常量构造函数的参数
必须是常量表达式
,且对象的字段必须是final
。 - 常量构造函数通常用于创建不变的、共享的对象,或者用于需要
高效内存使用和性能优化
的场景。
通过常量构造函数,你可以有效地管理和优化不可变对象,确保它们在整个应用中只有一个实例,并减少不必要的内存开销。
3. 总结
特性 | 命名构造函数 | 常量构造函数 |
---|---|---|
定义 | 通过类名后跟 . 来创建 |
通过在构造函数前加 const 关键字来定义 |
可变性 | 对象可以是可变的 | 对象必须是不可变的 |
参数 | 可以是运行时计算的值 | 参数必须是编译时常量 |
用途 | 提供多种初始化方式 | 创建可共享的、不可变的对象 |