方法(Method)
方法是为对象提供行为的函数。
实例方法
实例方法可以访问实例变量和this。下面的distanceTo()就是一个实例方法的例子:
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
Getters 和 Setters
Gettter 和 Setters 是为了能够读写对象属性的指定方法。回想一下,每个实例变量都有一个隐式getter,还有可能有一个setter。 你可以使用get和set关键字通过实现getter和setter来创建其他属性:
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算的属性: right 和 bottom.
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
使用getter和setter,你可以从实例变量开始,稍后使用方法包装它们,而无需更改客户端代码。
注意:无论是否明确定义了getter,增量(++)等运算符都以预期的方式工作。 为避免任何意外的副作用,运算符只需调用一次getter,将其值保存在临时变量中。
抽象方法
实例,getter和setter方法可以是抽象的,定义一个接口,但将其实现留给其他类。 抽象方法只能存在于抽象类中。(这个跟 java 里是一样的)
要使方法成为抽象,请使用分号(;)而不是方法体{}:
abstract class Doer {
// 定义实例变量和方法...
void doSomething(); // 定义一个抽象方法.
}
class EffectiveDoer extends Doer {
void doSomething() {
// 提供一个实现,所以这里的方法不是抽象的...
}
}
抽象类
使用abstract修饰符定义抽象类 - 无法实例化的类。 抽象类对于定义接口非常有用,通常还有一些实现。 如果希望抽象类看起来是可实例化的,请定义工厂构造函数。
抽象类通常有抽象方法。 下面是一个声明具有抽象方法的抽象类:
// 这个类定义了 abstract 不能被实例化
abstract class AbstractContainer {
// 定义构造函数,字段,方法...
void updateChildren(); // 抽象方法.
}
隐式接口
每个类都隐式定义一个接口,该接口包含该类的所有实例成员及其实现的任何接口。 如果要在不继承B实现的情况下创建支持B类API的A类,则A类应实现B接口。
类通过在implements子句中声明它们然后提供接口所需的API来实现一个或多个接口。 例如:
// A person. 隐式接口包含了 greet().
class Person {
// 在接口中, 但只有在这个库中可见
final _name;
// 不在接口中, 因为这是一个构造函数
Person(this._name);
// 在接口中
String greet(String who) => 'Hello, $who. I am $_name.';
}
// 一个Person 接口的实现
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
这是一个指定类实现多个接口的示例:
class Point implements Comparable, Location {...}
继承一个类
用extends去创建一个子类,用super去指向一个超类
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
重载成员
子类可以覆盖实例方法,getter和setter。 你可以使用@override注释来覆盖成员:
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
要在类型安全的代码中缩小方法参数或实例变量的类型,可以使用covariant关键字。
可覆盖的运算符
你可以覆盖下表中展示的运算符。 例如,如果定义Vector类,则可以定义一个+方法来添加两个向量。
< |
+ |
| |
[] |
|---|---|---|---|
> |
/ |
^ |
[]= |
<= |
~/ |
& |
~ |
>= |
* |
<< |
== |
- |
% |
>> |
注意:你可能已经注意到
!=不是可覆盖的运算符。 表达式e1!= e2只是!(e1==e2)的语法糖。
这是一个覆盖+和-运算符的类示例:
class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
如果覆盖==,则还应该覆盖Object的hashCode getter。(类似 java 中的覆写 equals(),还有 hashcode()) 有关重写==和hashCode的示例,请参阅实现映射键。
noSuchMethod()
要在代码尝试使用不存在的方法或实例变量时检测或做出反应,你可以覆盖noSuchMethod():
class A {
// 除非你覆盖了 noSuchMethod,
// 用一个不存在的成员会导致 NoSuchMethodError 结果.
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
除非满足以下条件之一,否则不能调用未实现的方法:
-
接收器具有静态类型
dynamic。 -
接收器有一个定义未实现方法的静态类型(抽象是ok的),接收器的动态类型有一个
noSuchMethod()实现,它与Object类中的实现不同。
有关更多信息,请参阅非正式noSuchMethod转发规范。
枚举类型
枚举类型(通常称为枚举或枚举)是一种特殊类,用于表示固定数量的常量值。
使用枚举
用enum申明一个枚举类型
enum Color { red, green, blue }
枚举中的每个值都有一个indexgetter,它返回枚举值的索引位置。 例如,第一个值具有索引0,第二个值具有索引1。
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
要获取枚举中所有值的列表,用枚举.values获取枚举的常量列表。
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
你可以在switch语句中使用枚举,如果不处理所有枚举值,将收到警告:
var aColor = Color.blue;
switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // 不写这个 default,你会看到WARNING.
print(aColor); // 'Color.blue'
}
枚举类型具有以下限制:
- 不能子类化,混合或实现枚举。
- 无法显式实例化枚举。
向类添加功能:mixins(混合)
混合是一种在多个类层次结构中重用类代码的方法。
使用混合的方法是with关键字后跟一个或多个mixin命名的类。 以下示例显示了两个使用mixins的类:
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
要定义一个mixin,首先创建一个mixin开头的Object扩展类,并且不能声明构造函数。 除非你希望mixin可用作常规类,否则请使用mixin关键字而不是class。 例如:
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
要指定只有某些类型可以使用mixin - 例如,mixin可以调用它没有定义的方法 - 使用on来指定所需的超类:
mixin MusicalPerformer on Musician {
// ···
}
版本说明:Dart 2.1中引入了对mixin关键字的支持。 早期版本中的代码通常使用抽象类。 有关2.1 mixin更改的更多信息,请参阅Dart SDK changelog和2.1 mixin规范。
类变量和方法
使用static关键字实现类范围的变量和方法。
Static 变量
静态变量(类变量)通常用作类范围的状态和常量:
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
静态变量在使用之前不会初始化。
注意:优先选择
lowerCamelCase作为常量名称。
Static 方法
静态方法(类方法)不对实例进行操作,因此无法使用this关键字。 例如:
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
static num distanceBetween(Point a, Point b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
}
void main() {
var a = Point(2, 2);
var b = Point(4, 4);
var distance = Point.distanceBetween(a, b);
assert(2.8 < distance && distance < 2.9);
print(distance);
}
注意:对于常用或广泛使用的实用程序和功能,请考虑使用顶级(top-level)函数而不是静态方法。
你可以使用静态方法作为编译时常量。 例如,可以将静态方法作为参数传递给常量构造函数。