方法(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
}
_bar
是一个私有的成员变量;- 定义一个 getter (
get bar
),它返回实例变量_bar
的值; - 定义一个 setter (
set bar
),通过它来修改_bar
的值; - 调用
get bar
这个getter方法 ; - 调用
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);
}
此例中的 bar2
由 bar
计算而成,像数学函数里的因变量。
下面来看一下如何使用 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
类的 height
由 perimeter
和 width
计算而成,通过它的setter修改其值,实际上是在修改width
的值 ,之所以为height
定义setter仅仅是为了计算上或概念理解上的方便。
抽象方法
抽象类可以拥有抽象方法。没有方法体(也就是没有具体的实现)的方法,称为抽象方法。 使用 abstract
关键字定义抽象类,例如:
// ex448.dart
abstract class Mammal {
void breathe();
}
此例中的 Mammal
是一个抽象类,它的 breathe()
为抽象方法。
抽象类及抽象方法的常见应用:
- 使用纯抽象类即接口(
interface
),定义API(应用编程接口); - 设计类层次,即类的分层设计;
- 抽象基类提供某个算法的骨架实现,子类只需实现抽象方法或覆盖某些方法即可,即实现设计模式里的模板方法。
下一章将更详细地讨论抽象类、抽象方法和接口等。