目錄

Dart 是一種物件導向語言,具有類別和基於混入的繼承。每個物件都是一個類別的實例,除了 Null 之外,所有類別都繼承自 Object基於混入的繼承表示雖然每個類別(除了 頂層類別 Object?)只有一個超類別,但類別主體可以在多個類別階層中重複使用。擴充方法 是一種在不變更類別或建立子類別的情況下,新增功能至類別的方法。類別修飾詞 允許您控制函式庫如何對類別進行子類型化。

使用類別成員

#

物件具有由函式和資料組成的成員(分別為方法實例變數)。當您呼叫方法時,您會在物件上呼叫它:方法可以存取該物件的函式和資料。

使用句點 (.) 來參照實例變數或方法

dart
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 時,使用 ?. 取代 . 以避免例外狀況

dart
// If p is non-null, set a variable equal to its y value.
var a = p?.y;

使用建構函式

#

您可以使用建構函式建立物件。建構函式名稱可以是 ClassNameClassName.identifier。例如,以下程式碼使用 Point()Point.fromJson() 建構函式建立 Point 物件

dart
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});

以下程式碼具有相同的效果,但在建構函式名稱之前使用選用的 new 關鍵字

dart
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});

有些類別提供 常數建構函式。若要使用常數建構函式建立編譯時期常數,請在建構函式名稱之前加上 const 關鍵字

dart
var p = const ImmutablePoint(2, 2);

建立兩個相同的編譯時期常數會產生一個單一的正規實例

dart
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!

常數內容中,您可以在建構函式或文字之前省略 const。例如,看看這段建立常數映射的程式碼

dart
// 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 關鍵字的所有使用,除了第一次使用之外

dart
// Only one const, which establishes the constant context.
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};

如果常數建構函式在常數內容之外,且在沒有 const 的情況下被呼叫,它會建立一個非常數物件

dart
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 物件。

dart
print('The type of a is ${a.runtimeType}');

到目前為止,您已經瞭解如何使用類別。本節的其餘部分說明如何實作類別。

執行個體變數

#

以下是宣告實例變數的方法

dart
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

dart
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

dart
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.fieldName` is a parameter declaration, not an expression:
  Point(this.x, this.y);
}

實例變數可以是 final,這種情況下必須設定一次。使用建構函式參數或建構函式的 初始化清單,在宣告時初始化 final、非 late 實例變數

dart
class ProfileMark {
  final String name;
  final DateTime start = DateTime.now();

  ProfileMark(this.name);
  ProfileMark.unnamed() : name = '';
}

如果您需要在建構函式主體開始後指定 final 實例變數的值,可以使用下列其中一項

隱含介面

#

每個類別都隱含地定義一個介面,其中包含類別的所有實例成員和它實作的任何介面。如果您要建立一個支援類別 B 的 API 但不繼承 B 的實作,類別 A 應該實作 B 介面。

類別透過在 implements 子句中宣告一個或多個介面,然後提供介面所需的 API 來實作這些介面。例如

dart
// 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()));
}

以下是指定類別實作多個介面的範例

dart
class Point implements Comparable, Location {...}

類別變數和方法

#

使用 static 關鍵字來實作類別範圍的變數和方法。

靜態變數

#

靜態變數(類別變數)對於類別範圍的狀態和常數很有用

dart
class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

靜態變數在使用之前不會初始化。

靜態方法

#

靜態方法(類別方法)不會對實例進行操作,因此無法存取 this。不過,它們可以存取靜態變數。如以下範例所示,您直接在類別上呼叫靜態方法

dart
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);
}

您可以將靜態方法用作編譯時期常數。例如,您可以將靜態方法傳遞為常數建構函式的參數。