跳到主要内容

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

使用 getset 可以把计算或校验封装为属性访问:

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 可以把已绑定的方法作为一等函数值传递。