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