方法(Method)

方法即类中定义的函数,有实例方法与静态方法之分,这在类的实例化与成员一节中已介绍过。 本文将介绍 Getter 与 Setter 方法抽象方法。 另外有一种特殊的方法是操作符,将在下一节中介绍。

Getter 与 Setter 方法

第2章讨论过 getter与 setter函数,getter 与 setter 方法与之类似,只不过方法定义在类中。

// ex411.dart
class Foo {
  Foo(this._bar);

  int _bar; // 1

  int get bar => _bar; // 2

  set bar(int value) => _bar = value; //3
}

void main() {
  var foo = Foo(100);
  print(foo.bar); // 4 Output: 100

  foo.bar = 200; // 5
  print(foo.bar); // Output: 200
}
  1. _bar是一个私有的成员变量;
  2. 定义一个 getter (get bar),它返回实例变量_bar的值;
  3. 定义一个 setter (set bar),通过它来修改_bar的值;
  4. 调用 get bar 这个getter方法 ;
  5. 调用 set bar 这个setter方法 。

语法上看,此例中的 bar 跟一个实例变量无异,实在没有必要专门定义 getter 和 setter ,即此例中Foo类等价于:

// ex412.dart
class Foo {
  Foo(this.bar);
  int bar; // 1
}

从方法的角度看,类中的公开字段(不以下划线开头)隐式地定义了对应的 getter 和 setter

何时使用 getter

使用 getter 的一个典型场景是计算型属性。这里的属性(property)即拥有getter的实例变量。

// ex413.dart
class Foo {
  Foo(this.bar);

  int bar; // 1

  int get bar2 => bar * bar;
}

void main() {
  var foo = Foo(1);
  assert(foo.bar2 == 1);

  foo.bar = 2;
  assert(foo.bar2 == 4);
}

此例中的 bar2bar 计算而成,像数学函数里的因变量。

下面来看一下如何使用 getter 定义只读属性。试想有一个代办任务类(Task),它的当前状态(status),只允许在 Task 内部修改,但允许类的外部代码访问,也就是说 status 对外是只读的。

// ex444.dart
class Task {
  Task(this._status) : assert(_status >= 0);
  int _status;

  int get status => _status;
}

何时使用 setter

代码总是在一次次迭代(Iteration,敏捷开发术语,指一个小的开发周期,比如2周)中完善。为方面用户使用Task类,又同时让其 _status 始终得到良好的维护,下面为 _status 添加一个setter。

// ex445.dart
class Task {
  // ... (omitted for brevity)

  set status(int value) {
    if (value > 0) {
      _status = value;
    }
  }
}

此例中的 set status 在 修改_status 前进行了校验,这便是有条件地更新。还可以在setter中添加一些附加操作,例如打印日志。完整的示例如下:

// ex446.dart
class Task {
  Task(this._status) : assert(_status >= 0);
  int _status;
  
  int get status => _status;

  set status(int value) {
    if (value > 0) {
      final old = _status;
      _status = value;
      print('status changed from $old to $_status');
    }
  }
}

void main() {
  var t = Task(0);
  t.status = 1; // Output: status changed from 0 to 1
}

有时两个或多个实例变量之间有一定的约束关系。比如指定一个长方形的周长,那么长+宽的值便是固定的(等于半周长)。

// ex447.dart
class Rect {
  Rect(this.perimeter, this.width);

  final int perimeter;
  int width;

  int get height => perimeter ~/ 2 - width;
  set height(int value) => width = perimeter ~/ 2 - value;
}

void main() {
  var rect = Rect(100, 10);
  assert(rect.width == 10);
  assert(rect.height == 40);

  rect.width = 20;
  assert(rect.width == 20);
  assert(rect.height == 30);

  rect.height = 20;
  assert(rect.width == 30);
  assert(rect.height == 20);
}

此例中Rect类的 heightperimeterwidth 计算而成,通过它的setter修改其值,实际上是在修改width的值 ,之所以为height定义setter仅仅是为了计算上或概念理解上的方便

抽象方法

抽象类可以拥有抽象方法。没有方法体(也就是没有具体的实现)的方法,称为抽象方法。 使用 abstract 关键字定义抽象类,例如:

// ex448.dart
abstract class Mammal {
   void breathe();
}

此例中的 Mammal 是一个抽象类,它的 breathe() 为抽象方法。

抽象类及抽象方法的常见应用:

  • 使用纯抽象类即接口(interface),定义API(应用编程接口);
  • 设计类层次,即类的分层设计;
  • 抽象基类提供某个算法的骨架实现,子类只需实现抽象方法或覆盖某些方法即可,即实现设计模式里的模板方法

下一章将更详细地讨论抽象类、抽象方法和接口等。