方法是為對(duì)象提供行為的函數(shù)。
對(duì)象的實(shí)例方法可以訪問 this 和實(shí)例變量。 以下示例中的 distanceTo() 方法就是實(shí)例方法:
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);
}
}
Getter 和 Setter 是用于對(duì)象屬性讀和寫的特殊方法。 回想之前的例子,每個(gè)實(shí)例變量都有一個(gè)隱式 Getter ,通常情況下還會(huì)有一個(gè) Setter 。 使用 get 和 set 關(guān)鍵字實(shí)現(xiàn) Getter 和 Setter ,能夠?yàn)閷?shí)例創(chuàng)建額外的屬性。
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定義兩個(gè)計(jì)算屬性: 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);
}
最開始實(shí)現(xiàn) Getter 和 Setter 也許是直接返回成員變量; 隨著需求變化, Getter 和 Setter 可能需要進(jìn)行計(jì)算處理而使用方法來(lái)實(shí)現(xiàn); 但是,調(diào)用對(duì)象的代碼不需要做任何的修改。
提示: 類似 (++) 之類操作符不管是否定義了 getter 方法,都能夠正確的執(zhí)行。 為了避免一些問題,操作符只調(diào)用一次 getter 方法, 然后把值保存到一個(gè)臨時(shí)的變量中。
實(shí)例方法, getter, 和 setter 方法可以是抽象的, 只定義接口不進(jìn)行實(shí)現(xiàn),而是留給其他類去實(shí)現(xiàn)。 抽象方法只存在于 抽象類 中。
定義一個(gè)抽象函數(shù),使用分號(hào) (;) 來(lái)代替函數(shù)體:
abstract class Doer {
// 定義實(shí)例變量和方法 ...
void doSomething(); // 定義一個(gè)抽象方法。
}
class EffectiveDoer extends Doer {
void doSomething() {
// 提供方法實(shí)現(xiàn),所以這里的方法就不是抽象方法了...
}
}
調(diào)用抽象方法會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。
使用 abstract 修飾符來(lái)定義 抽象類 — 抽象類不能實(shí)例化。 抽象類通常用來(lái)定義接口,以及部分實(shí)現(xiàn)。 如果希望抽象類能夠被實(shí)例化,那么可以通過定義一個(gè) 工廠構(gòu)造函數(shù) 來(lái)實(shí)現(xiàn)。
抽象類通常具有 抽象方法。 下面是一個(gè)聲明具有抽象方法的抽象類示例:
// 這個(gè)類被定義為抽象類,
// 所以不能被實(shí)例化。
abstract class AbstractContainer {
// 定義構(gòu)造行數(shù),字段,方法...
void updateChildren(); // 抽象方法。
}
每個(gè)類都隱式的定義了一個(gè)接口,接口包含了該類所有的實(shí)例成員及其實(shí)現(xiàn)的接口。 如果要?jiǎng)?chuàng)建一個(gè) A 類,A 要支持 B 類的 API ,但是不需要繼承 B 的實(shí)現(xiàn), 那么可以通過 A 實(shí)現(xiàn) B 的接口。
一個(gè)類可以通過 implements 關(guān)鍵字來(lái)實(shí)現(xiàn)一個(gè)或者多個(gè)接口, 并實(shí)現(xiàn)每個(gè)接口要求的 API。 例如:
// person 類。 隱式接口里面包含了 greet() 方法聲明。
class Person {
// 包含在接口里,但只在當(dāng)前庫(kù)中可見。
final _name;
// 不包含在接口里,因?yàn)檫@是一個(gè)構(gòu)造函數(shù)。
Person(this._name);
// 包含在接口里。
String greet(String who) => 'Hello, $who. I am $_name.';
}
// person 接口的實(shí)現(xiàn)。
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()));
}
下面示例演示一個(gè)類如何實(shí)現(xiàn)多個(gè)接口: Here’s an example of specifying that a class implements multiple interfaces:
class Point implements Comparable, Location {...}
使用 extends 關(guān)鍵字來(lái)創(chuàng)建子類, 使用 super 關(guān)鍵字來(lái)引用父類:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
子類可以重寫實(shí)例方法,getter 和 setter。 可以使用 @override 注解指出想要重寫的成員:
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
To narrow the type of a method parameter or instance variable in code that is type safe, you can use the covariant keyword.
下標(biāo)的運(yùn)算符可以被重寫。 例如,想要實(shí)現(xiàn)兩個(gè)向量對(duì)象相加,可以重寫 + 方法。
< | + | | | [] |
> | / | ^ | []= |
<= | ~/ | & | ~ |
>= | * | << | == |
– | % | >> |
提示: 你可能會(huì)被提示 != 運(yùn)算符為非可重載運(yùn)算符。 因?yàn)?nbsp;e1 != e2 表達(dá)式僅僅是 !(e1 == e2) 的語(yǔ)法糖。
下面示例演示一個(gè)類重寫 + 和 - 操作符:
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);
// 運(yùn)算符 == 和 hashCode 部分沒有列出。 有關(guān)詳情,請(qǐng)參考下面的注釋。
// ···
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
如果要重寫 == 操作符,需要重寫對(duì)象的 hashCode getter 方法。 重寫 == 和 hashCode 的實(shí)例,參考 Implementing map keys.
有關(guān)重寫的更多介紹,請(qǐng)參考 擴(kuò)展類(繼承).
當(dāng)代碼嘗試使用不存在的方法或?qū)嵗兞繒r(shí), 通過重寫 noSuchMethod() 方法,來(lái)實(shí)現(xiàn)檢測(cè)和應(yīng)對(duì)處理:
class A {
// 如果不重寫 noSuchMethod,訪問
// 不存在的實(shí)例變量時(shí)會(huì)導(dǎo)致 NoSuchMethodError 錯(cuò)誤。
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
除非符合下面的任意一項(xiàng)條件, 否則沒有實(shí)現(xiàn)的方法不能夠被調(diào)用:
有關(guān)更多信息,參考 noSuchMethod forwarding specification.
枚舉類型也稱為 enumerations 或 enums , 是一種特殊的類,用于表示數(shù)量固定的常量值。
使用 enum 關(guān)鍵字定義一個(gè)枚舉類型:
enum Color { red, green, blue }
枚舉中的每個(gè)值都有一個(gè) index getter 方法, 該方法返回值所在枚舉類型定義中的位置(從 0 開始)。 例如,第一個(gè)枚舉值的索引是 0 , 第二個(gè)枚舉值的索引是 1。
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
使用枚舉的 values 常量, 獲取所有枚舉值列表( list )。
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
可以在 switch 語(yǔ)句 中使用枚舉, 如果不處理所有枚舉值,會(huì)收到警告:
var aColor = Color.blue;
switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // 沒有這個(gè),會(huì)看到一個(gè)警告。
print(aColor); // 'Color.blue'
}
枚舉類型具有以下限制:
有關(guān)更多信息,參考 Dart language specification 。
Mixin 是復(fù)用類代碼的一種途徑, 復(fù)用的類可以在不同層級(jí),之間可以不存在繼承關(guān)系。
通過 with 后面跟一個(gè)或多個(gè)混入的名稱,來(lái) 使用 Mixin , 下面的示例演示了兩個(gè)使用 Mixin 的類:
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
通過創(chuàng)建一個(gè)繼承自 Object 且沒有構(gòu)造函數(shù)的類,來(lái) 實(shí)現(xiàn) 一個(gè) Mixin 。 如果 Mixin 不希望作為常規(guī)類被使用,使用關(guān)鍵字 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 可以調(diào)用 Mixin 自身沒有定義的方法 - 使用 on 來(lái)指定可以使用 Mixin 的父類類型:
mixin MusicalPerformer on Musician {
// ···
}
版本提示: mixin
關(guān)鍵字在 Dart 2.1 中被引用支持。 早期版本中的代碼通常使用 abstract class
代替。 更多有關(guān) Mixin 在 2.1 中的變更信息,請(qǐng)參見 Dart SDK changelog 和 2.1 mixin specification 。
提示: 對(duì) Mixin 的一些限制正在被移除。 關(guān)于更多詳情,參考 proposed mixin specification.
有關(guān) Dart 中 Mixin 的理論演變,參考 A Brief History of Mixins in Dart.
使用 static 關(guān)鍵字實(shí)現(xiàn)類范圍的變量和方法。
靜態(tài)變量(類變量)對(duì)于類級(jí)別的狀態(tài)是非常有用的:
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
靜態(tài)變量只到它們被使用的時(shí)候才會(huì)初始化。
提示: 代碼準(zhǔn)守風(fēng)格推薦指南 中的命名規(guī)則, 使用 lowerCamelCase 來(lái)命名常量。
靜態(tài)方法(類方法)不能在實(shí)例上使用,因此它們不能訪問 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);
}
提示: 對(duì)于常見或廣泛使用的工具和函數(shù), 應(yīng)該考慮使用頂級(jí)函數(shù)而不是靜態(tài)方法。
靜態(tài)函數(shù)可以當(dāng)做編譯時(shí)常量使用。 例如,可以將靜態(tài)方法作為參數(shù)傳遞給常量構(gòu)造函數(shù)。
更多建議: