類
Dart 是一種面向物件的語言,支援類和基於混入(mixin)的繼承。每個物件都是類的例項,除了 Null 之外的所有類都繼承自 Object。基於混入的繼承意味著雖然每個類(除了頂層類 Object?)都只有一個超類,但類體可以在多個類層次結構中複用。擴充套件方法是一種在不改變類或建立子類的情況下向類新增功能的方式。類修飾符允許你控制庫如何子型別化一個類。
使用類成員
#物件擁有由函式和資料組成的成員(分別為方法和例項變數)。當你呼叫一個方法時,你是在物件上呼叫它:該方法可以訪問該物件的函式和資料。
使用點 (.) 來引用例項變數或方法
var p = Point(2, 2);
// Get the value of y.
assert(p.y == 2);
// Invoke distanceTo() on p.
double distance = p.distanceTo(Point(4, 4));使用 ?. 而不是 . 來避免左側運算元為 null 時發生異常
// If p is non-null, set a variable equal to its y value.
var a = p?.y;使用建構函式
#你可以使用建構函式建立物件。建構函式名稱可以是 ClassName 或 ClassName.identifier。例如,以下程式碼使用 Point() 和 Point.fromJson() 建構函式建立 Point 物件
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});以下程式碼具有相同的效果,但使用可選的 new 關鍵字在建構函式名稱前
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});某些類提供常量建構函式。要使用常量建構函式建立編譯時常量,請將 const 關鍵字放在建構函式名稱前
var p = const ImmutablePoint(2, 2);構造兩個相同的編譯時常量會得到一個單一的規範例項
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!在常量上下文中,你可以省略建構函式或字面量前的 const。例如,請看這段建立常量對映的程式碼
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};你可以省略除第一次使用 const 關鍵字以外的所有情況
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};如果一個常量建構函式在常量上下文之外且沒有使用 const 呼叫,它會建立一個非常量物件
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant
assert(!identical(a, b)); // NOT the same instance!獲取物件的型別
#要在執行時獲取物件的型別,你可以使用 Object 屬性 runtimeType,它會返回一個 Type 物件。
print('The type of a is ${a.runtimeType}');至此,你已經瞭解瞭如何使用類。本節的其餘部分將展示如何實現類。
例項變數
#以下是如何宣告例項變數
class Point {
double? x; // Declare instance variable x, initially null.
double? y; // Declare y, initially null.
double z = 0; // Declare z, initially 0.
}使用可空型別宣告的未初始化例項變數的值為 null。非空例項變數必須在宣告時初始化。
所有例項變數都會生成一個隱式的getter方法。非 final 例項變數和沒有初始化器的 late final 例項變數也會生成一個隱式的setter方法。有關詳細資訊,請參閱Getter 和 Setter。
class Point {
double? x; // Declare instance variable x, initially null.
double? y; // Declare y, initially null.
}
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}在宣告處初始化非 late 例項變數會在例項建立時設定其值,這發生在建構函式及其初始化列表執行之前。因此,非 late 例項變數的初始化表示式(在 = 之後)不能訪問 this。
double initialX = 1.5;
class Point {
// OK, can access declarations that do not depend on `this`:
double? x = initialX;
// ERROR, can't access `this` in non-`late` initializer:
double? y = this.x;
// OK, can access `this` in `late` initializer:
late double? z = this.x;
// OK, `this.x` and `this.y` are parameter declarations, not expressions:
Point(this.x, this.y);
}例項變數可以是 final 型別,在這種情況下它們必須且只能被設定一次。初始化 final、非 late 例項變數可以在宣告時進行,也可以透過建構函式引數或使用建構函式的初始化列表進行
class ProfileMark {
final String name;
final DateTime start = DateTime.now();
ProfileMark(this.name);
ProfileMark.unnamed() : name = '';
}如果你需要在建構函式體開始後為 final 例項變數賦值,可以使用以下方法之一
隱式介面
#每個類都隱式定義了一個介面,其中包含該類的所有例項成員以及它實現的任何介面的成員。如果你想建立一個類 A,它支援類 B 的 API 而不繼承 B 的實現,那麼類 A 應該實現 B 介面。
一個類透過在 implements 子句中宣告一個或多個介面,然後提供介面所需的 API 來實現它們。例如
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final String _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.';
}
// An implementation of the Person interface.
class Impostor implements Person {
String 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 {
...
}類變數與方法
#使用 static 關鍵字來實現類範圍的變數和方法。
靜態變數
#靜態變數(類變數)對於類範圍的狀態和常量非常有用
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}靜態變數在使用前不會被初始化。
靜態方法
#靜態方法(類方法)不作用於例項,因此無法訪問 this。但是,它們可以訪問靜態變數。如下例所示,你可以直接在類上呼叫靜態方法
import 'dart:math';
class Point {
double x, y;
Point(this.x, this.y);
static double 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);
}你可以將靜態方法用作編譯時常量。例如,你可以將靜態方法作為引數傳遞給常量建構函式。