Methods(方法)
方法是定义在类、枚举、Mixin 或扩展中的函数。实例方法操作具体对象的状态,静态方法属于类型本身;getter、setter 和运算符方法则用专门语法提供属性访问与运算行为。
实例方法
实例方法在对象上调用,可以直接访问该对象的实例字段和其他实例方法:
class BankAccount {
double balance;
BankAccount(this.balance);
void deposit(double amount) {
if (amount <= 0) {
throw ArgumentError.value(amount, 'amount', 'must be positive');
}
balance += amount;
}
bool canWithdraw(double amount) => amount <= balance;
}
void main() {
final account = BankAccount(100);
account.deposit(50);
print(account.canWithdraw(120)); // true
}
只有一个表达式的方法可以使用 =>。箭头右侧必须是表达式,不能直接放语句。
this 的使用
实例方法中的未限定成员默认属于当前对象,通常不必写 this。当参数或局部变量与字段同名时,用 this 消除歧义:
class Profile {
String name;
Profile(this.name);
void rename(String name) {
this.name = name;
}
}
静态方法没有当前实例,因此不能使用 this,也不能直接访问实例字段。
Getter 与 setter
使用 get 和 set 可以把计算或校验封装为属性访问:
class Temperature {
double _celsius;
Temperature(this._celsius);
double get celsius => _celsius;
double get fahrenheit => _celsius * 9 / 5 + 32;
set celsius(double value) {
if (value < -273.15) {
throw ArgumentError('Below absolute zero');
}
_celsius = value;
}
}
void main() {
final temperature = Temperature(20);
print(temperature.fahrenheit); // 68.0
temperature.celsius = 25;
print(temperature.celsius); // 25.0
}
调用 getter 时不加括号,调用 setter 时使用赋值语法。getter 应适合快速、无参数地读取值;带有明显动作、异步过程或昂贵计算的操作通常更适合普通方法。
运算符方法
Dart 允许类重写一组既定运算符。运算符声明使用 operator 关键字:
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
Vector operator +(Vector other) {
return Vector(x + other.x, y + other.y);
}
Vector operator -() => Vector(-x, -y);
bool operator ==(Object other) {
return other is Vector && x == other.x && y == other.y;
}
int get hashCode => Object.hash(x, y);
}
void main() {
print(Vector(1, 2) + Vector(3, 4) == Vector(4, 6)); // true
}
不能创建任意新运算符,也不能改变运算符的优先级。重写 == 时必须同步维护 hashCode,保证相等对象拥有相同哈希值。
抽象方法
抽象方法只有签名,没有实现,必须位于抽象类或 Mixin 等允许抽象成员的声明中:
abstract class Shape {
double area();
}
class Square extends Shape {
final double side;
Square(this.side);
double area() => side * side;
}
具体子类必须实现继承到的抽象成员,否则它自身也必须声明为抽象类。
静态方法
静态方法通过类名调用,适合不依赖对象状态、且概念上属于该类型的操作:
class Distance {
static double between(double start, double end) {
return (end - start).abs();
}
}
void main() {
print(Distance.between(3, 10)); // 7.0
}
不要仅为了缩短调用而把普通工具函数都塞进类中。与某个类型没有紧密关系的函数可以保持为顶层函数。
方法 tear-off
引用方法但不立即调用,会得到一个函数对象:
class Formatter {
String uppercase(String value) => value.toUpperCase();
}
void main() {
final formatter = Formatter();
final format = formatter.uppercase;
print(['dart', 'flutter'].map(format).toList());
// [DART, FLUTTER]
}
实例方法 tear-off 会绑定接收对象。上例中的 format 始终调用 formatter 实例的方法。
TypeScript 对比:两种语言都有实例方法、静态方法、getter 和 setter。Dart 还直接支持运算符方法;TypeScript 不能为自定义类重载
+等运算符。Dart 的实例方法 tear-off 会绑定接收者,也不同于 JavaScript 中直接取出方法后可能丢失this的行为。
常见误区
- setter 只能接收一个参数,并通过赋值语法调用。
- 静态方法不能直接读取实例字段,因为它没有
this。 - 方法 tear-off 已经是函数值,不需要再包一层只负责转发参数的闭包。
==与hashCode是一组契约,不能只重写其中一个。
小结
- 实例方法操作当前对象,静态方法属于类型本身。
- getter 和 setter 用属性语法封装读取、计算与赋值校验。
- 运算符方法只能重写 Dart 支持的运算符,并应保持符合直觉的语义。
- 抽象方法规定接口,由具体子类提供实现。
- 方法 tear-off 可以把已绑定的方法作为一等函数值传递。