Methods
实例方法
对象的实例方法可以访问实例变量和this。以下示例中的distanceTo()方法是实例方法的例子:
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
// 这个方法中distanceTo可以访问this.x和this.y
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
Point point = Point(4, 6);
Point anthorPoint = Point(1, 2);
print(point.distanceTo(anthorPoint)); // 5
Getters and setters
getter和setter是提供对象属性的读写访问权限的特殊方法。回想一下,其实每个实例变量都有一个隐式getter,如果设置恰当的话还有一个setter。可以使用get和set关键字通过实现getter和setter来创建其他属性:
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义了两个计算属性: right and 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);
// 当给.right赋值时,会执行set right对应的方法
rect.right = 12; // left = 12 - 20
assert(rect.left == -8);
}
使用getter和setter,可以从实例变量开始,稍后使用方法包装它们,而无需更改代码。
无论是否明确定义了getter,增量(++)等运算符都以预期的方式工作。为避免任何意外的副作用,++操作符只会调用一次getter,将其值保存在临时变量中。
抽象方法
实例、getter和setter方法可以是抽象的,定义一个接口,但将其实现留给其他类。抽象方法只能存在于抽象类(abstract classes)中:
要使方法成为abstract,请使用分号(;)结尾而不是方法体:
abstract class Doer {
void doSomething(); // 定义抽象方法,只能在抽象类中定义,具体实现留给其他类
}
class EffectiveDoer extends Doer {
void doSomething() {
方法实现,这不是一个抽象方法。
}
}
抽象类
使用abstract修饰符定义抽象类 ----- 无法实例化的类。抽象类对于定义接口非常有用,通常还有一些实现。如果希望抽象类看起来是可实例化的,请定义工厂构造函数(factory constructor):
// 这个类被定义为抽象类无法直接实例化,可以提供工厂构造函数来实例化
abstract class AbstractContainer {
void updateChildren(); // 抽象方法
}
隐式接口
每个类都隐式定义了一个接口,该接口包含该类的所有实例成员及其实现的任何接口。如果要在不继承B实现的情况下创建支持B类API的A类,则A类应实现B接口。
// A person 包含隐式的greet方法.
class Person {
// In the interface, but visible only in this library.
final _name;
// Not in the interface, since this is a constructor.
Person(this._name);
// In the interface.
String greet(String who) => 'Hello, $who. I am $_name.';
}
// person接口的实现.
class Impostor implements Person {
get _name => '';
// 自己实现greet方法
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'))); // Hello, Bob. I am Kathy.
print(greetBob(Impostor())); // Hi Bob. Do you know who I am?
}
这是一个指定类实现多个接口的示例:
class Point implements Comparable, Location {...}
继承一个类
使用extends创建子类,使用super来引用超类:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
// 继承Television类
class SmartTelevision extends Television {
void turnOn() {
// super调用超类方法。
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
重写成员
子类可以覆盖实例方法,getter和setter。你可以使用@override注释来标明有意覆盖成员:
// 前面的SmartTelevision类中就重写了Television中的turnOn方法
// @override只是一个标识符,没有实际意义。
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
覆盖操作符
之前介绍操作符是已经说过,操作符是可以覆盖的。例如,如果定义Vector类,则可以定义一个+方法来添加两个vectors:
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)); // v + w 将执行 (Vector v) => Vector(x + v.x, y + v.y)
assert(v - w == Vector(0, 1)); // v - w 将执行 (Vector v) => Vector(x - v.x, y - v.y)
}
如果覆盖==,则还应该覆盖Object的hashCode getter。有关重写==和hashCode的示例,请参阅Implementing map keys。
noSuchMethod():
要在代码尝试使用不存在的方法或实例变量时检测或做出反应,可以覆盖noSuchMethod():
class A {
// 除非你覆盖了noSuchMethod方法,否则会在使用不存在的方法时报错
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
除非满足以下条件之一,否则不能调用未实现的方法:
- 接收者具有静态类型dynamic
- 接收者有一个定义未实现方法的静态类型(比如
abstract),接收器的动态类型有一个noSuchMethod()的实现,它与类Object中的实现不同.
示例1:
main() {
dynamic person = new Person(); // person 声明为dynamic,满足了第一个条件
print(person.missing('shubham')); // 调用了不存在的方法missing
// 打印结果: Got the Symbol("missing") with arguments [shubham]
}
class Person {
@override //overring noSuchMethod
noSuchMethod(Invocation invocation) => 'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}
示例2:
class Person {
// 定义了一个没有实现方法体的方法,前面说过抽象方法只能存在抽象类中,但是这里定义了noSuchMethod,所以可以存在
missing(int age,String name);
@override //overriding noSuchMethod
noSuchMethod(Invocation invocation) => 'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}
main() {
dynamic person = new Person(); // person 声明为dynamic
print(person.missing('shubham')); // calling abstract method
// 打印结果: Got the Symbol("missing") with arguments [shubham]
}
枚举类型
使用枚举类
使用enum关键字声明枚举类型:
enum Color { red, green, blue }
枚举中的每个值都有一个索引getter,它返回枚举声明中值的从零开始的位置。例如,第一个值具有索引0,第二个值具有索引1:
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
要获取枚举中所有值的List,请使用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: // 没有这句话,会收到一条警告
print(aColor); // 'Color.blue'
}
枚举类型具有以下限制:
mixins
Mixins是一种在多个类层次结构中重用类代码的方法(由于dart是单继承类,所以对于想复用多个类,请使用mixins)。
维基百科中定义:
在面向对象的语言中,mixins类是一个可以把自己的方法提供给其他类使用,但却不需要成为其他类的父类。
要使用mixin,请使用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,请创建一个继承自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,使用on来指定所需的超类:
mixin MusicalPerformer on Musician {
// ···
}
类变量和方法
Static variables
静态变量(类变量)对于类范围的状态和常量很有用:
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
// 直接在类上调用静态变量,而不需要实例化
assert(Queue.initialCapacity == 16);
}
Static methods
静态方法(类方法)不对实例进行操作,因此无法访问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);
print(distance); // 2.8284271247461903
}
对于常用或广泛使用的实用工具类和功能,请考虑使用顶级函数而不是静态方法。
你可以使用静态方法作为编译时常量。例如,可以将静态方法作为参数传递给常量构造函数。