Dart 的函数、类与运算符
本文将介绍 Dart 编程中的函数、类与运算符的基本概念和用法,帮助零基础的读者快速入门 Dart 语言。
函数
函数是一段用于独立完成某个功能的代码。在 Dart 中,所有类型都是对象,包括函数。函数的类型叫作 Function
,这意味着函数也可以被定义为变量,甚至可以作为参数传递给另一个函数。
函数的定义和使用
以下是一个简单的函数示例:
bool isZero(int number) {
return number == 0;
}
void printInfo(int number, Function check) {
print("$number is Zero: ${check(number)}");
}
Function f = isZero;
int x = 10;
int y = 0;
printInfo(x, f); // 输出 10 is Zero: false
printInfo(y, f); // 输出 0 is Zero: true
可以使用箭头函数来简化单行函数的定义:
bool isZero(int number) => number == 0;
可选参数和命名参数
Dart 不支持函数重载,但提供了可选命名参数和可选参数的方式,使函数的参数声明更加优雅和可维护。
- 可选命名参数使用
{}
包裹,可选命名参数使用{}
包裹。调用函数时,可以通过paramName: value
的方式来传递参数。未传递的参数将为null
。:
void enableFlags({bool bold, bool hidden}) {
print("$bold, $hidden");
}
enableFlags(bold: true, hidden: false); // 输出 true, false
enableFlags(bold: true); // 输出 true, null
- 命名参数的默认值 在定义函数时,可以为可选命名参数提供默认值,这样即使调用时未传递该参数,也会使用默认值:
void enableFlags({bool bold = true, bool hidden = false}) {
print("$bold, $hidden");
}
enableFlags(); // 输出 true, false
enableFlags(bold: false); // 输出 false, false
enableFlags(hidden: true); // 输出 true, true
- 可选参数使用
[]
包裹,调用函数时,可以省略这些参数。未传递的参数将为 null。:
void enableFlags(bool bold, [bool hidden]) {
print("$bold, $hidden");
}
enableFlags(true, false); // 输出 true, false
enableFlags(true); // 输出 true, null
- 可选参数的默认值 在定义函数时,可以为可选参数提供默认值,这样即使调用时未传递该参数,也会使用默认值:
void enableFlags(bool bold, [bool hidden = false]) {
print("$bold, $hidden");
}
enableFlags(true, false); // 输出 true, false
enableFlags(true); // 输出 true, false
可选命名参数与可选参数的区别
可选命名参数 使用 {}
包裹,调用时必须使用 paramName: value
的方式,参数顺序可以不固定,且可读性高。
可选参数 使用 []
包裹,调用时按顺序传递参数,省略的参数将为 null,相比之下可读性较低,但更简洁。
综合示例:
void describePerson(String name, {int age = 30, String gender = 'unknown'}) {
print("Name: $name, Age: $age, Gender: $gender");
}
describePerson('Alice'); // 输出 Name: Alice, Age: 30, Gender: unknown
describePerson('Bob', age: 25); // 输出 Name: Bob, Age: 25, Gender: unknown
describePerson('Charlie', gender: 'male'); // 输出 Name: Charlie, Age: 30, Gender: male
void enableFeatures(String feature, [bool isEnabled = true, String level = 'basic']) {
print("Feature: $feature, Enabled: $isEnabled, Level: $level");
}
enableFeatures('Dark Mode'); // 输出 Feature: Dark Mode, Enabled: true, Level: basic
enableFeatures('Notifications', false); // 输出 Feature: Notifications, Enabled: false, Level: basic
enableFeatures('Security', true, 'advanced'); // 输出 Feature: Security, Enabled: true, Level: advanced
类
类是特定类型的数据和方法的集合,也是创建对象的模板。Dart 是面向对象的语言,每个对象都是一个类的实例,都继承自顶层类型 Object。
类的定义及初始化
以下是一个类的定义示例:
class Point {
num x, y; // 成员变量
static num factor = 0; // 静态变量
// 构造函数
Point(this.x, this.y);
// 实例方法
void printInfo() => print('($x, $y)');
// 静态方法
static void printZValue() => print('$factor');
}
// 创建类的实例
var p = Point(100, 200);
p.printInfo(); // 输出 (100, 200)
// 访问和修改静态变量
Point.factor = 10;
Point.printZValue(); // 输 出 10
详解:
- 成员变量:
x
和y
是实例变量,每个实例都有独立的这些变量。 - 静态变量:
factor
是静态变量,属于类本身,而不是某个具体的实例。 - 构造函数:
Point(this.x, this.y)
是类的构造函数,用于初始化成员变量x
和y
。this.x
表示当前实例的 x 变量。 - 实例方法:
printInfo()
是实例方法,用于打印实例的x
和y
值。 - 静态方法:
printZValue()
是静态方法,只能访问静态变量factor
。
命名构造函数与初始化列表
在 Dart 中,类的构造函数不仅用于创建对象,还可以用来初始化对象的成员变量。Dart 提供了命名构造函数和初始化列表来实现灵活的对象初始化。
- 命名构造函数
命名构造函数:允许为类定义多个构造函数,以不同的方式初始化对象。命名构造函数通过类名后面的 点号和构造函数名称来定义。
- 初始化列表
初始化列表用于在构造函数体执行之前初始化成员变量。初始化列表在构造函数参数列表之后、构造函数体之前,用冒号
:
分隔。
class Point {
num x, y, z;
// 默认构造器 使用初始化列表初始化变量 z
Point(this.x, this.y) : z = 0;
// 命名构造函数
Point.bottom(num x) : this(x, 0);
// 实例方法
void printInfo() => print('($x, $y, $z)');
}
// 使用命名构造函数创建实例
var p = Point.bottom(100);
p.printInfo(); // 输出 (100, 0, 0)
详解:
- 初始化列表:在默认构造函数
Point(this.x, this.y)
后面使用: z = 0
初始化成员变量z
。Point(this.x, this.y) : z = 0;
- 命名构造函数:
Point.bottom(num x)
是命名构造函数,它调用默认构造函数Point(x, 0)
来初始化对象,将x
初始化为传入的参数x,y
初始化为 0。Point.bottom(num x) : this(x, 0);
类的复用
在面向对象的编程中,类的复用是通过继承、接口实现和混入 (Mixin) 来实现的。Dart 提供了这些机制来促进代码的复用和组织。
继承
继承是复用代码的最常见方式,子类从父类继承成员变量和方法。子类可以重写父类的方法,也可以添加自己的新功能。
class Point {
num x = 0, y = 0;
void printInfo() => print('($x, $y)');
}
// Vector 继承自 Point
class Vector extends Point {
num z = 0;
@override
void printInfo() => print('($x, $y, $z)');
}
var v = Vector();
v.x = 3;
v.y = 4;
v.z = 5;
v.printInfo(); // 输出 (3, 4, 5)
详解:
- 父类 (Point):定义了成员变量
x
和y
,以及实例方法printInfo
。 - 子类 (Vector):继承自
Point
,添加了新的成员变量z
,并重写了printInfo
方法。
接口实现
Dart 中的接口是隐式的,任何类都可以作为接口实现。实现接口的类必须提供接口中定义的所有方法。
class Point {
num x = 0, y = 0;
void printInfo() => print('($x, $y)');
}
// Coordinate 实现了 Point 接口
class Coordinate implements Point {
num x = 0, y = 0;
@override
void printInfo() => print('($x, $y)');
}
var c = Coordinate();
c.x = 5;
c.y = 10;
c.printInfo(); // 输出 (5, 10)
解释:
- 接口 (Point):定义了成员变量
x
和y
,以及实例方法printInfo
。 - 实现类 (Coordinate):实现了
Point
接口,必须定义接口中的所有成员。
混入 (Mixin)
混入是 Dart 提供的一种机制,用于在类之间共享行为,而不需要继承。混入使用 with 关键字,将一个类的功能添加到另一个类中。
class Point {
num x = 0, y = 0;
void printInfo() => print('($x, $y)');
}
mixin Z {
num z = 0;
void printZInfo() => print('z = $z');
}
// 使用混入
class Vector with Point, Z {
@override
void printInfo() => print('($x, $y, $z)');
}
var v = Vector();
v.x = 3;
v.y = 4;
v.z = 5;
v.printInfo(); // 输出 (3, 4, 5)
v.printZInfo(); // 输出 z = 5
解释:
- 父类 (Point):定义了成员变量
x
和y
,以及实例方法printInfo
。 - 混入 (Mixin Z):定义了成员变量
z
和方法printZInfo
。 - 子类 (Vector):通过 with 关键字混入了
Point
和Z
的行为,重写了printInfo
方法。
复用机制的选择
- 继承:用于需要复用父类方法实现的场景。子类会继承父类的所有成员,并可以重写父类的方法。
- 接口实现:用于需要定义接口并强制实现其所有方法的场景。实现类只需要提供接口的方法实现。
- 混入 (Mixin):用于在类之间共享行为而不需要继承的场景。混入可以避免多重继承的复杂性,提供灵活的代码复用方式。
运算符
Dart 提供了与其他编程语言类似的运算符 ,并增加了几个用于简化处理变量实例缺失的运算符:
?.
运算符:用于在调用对象的成员方法时避免空指针异常。
Point p;
p?.printInfo(); // 如果 p 为 null,则跳过调用
??=
运算符:用于在变量为 null 时赋值。
var a;
a ??= 10; // 如果 a 为 null,则赋值 10
??
运算符:用于在变量为 null 时返回另一个值。
var a;
var b = a ?? 10; // 如果 a 为 null,则 b 为 10