內容

透過建立與其類別同名的函式(加上額外的識別碼,如 命名建構函式 中所述,但這是選用的)來宣告建構函式。

使用最常見的建構函式,即產生式建構函式,來建立類別的新執行個體,並初始化正式參數以實例化任何執行個體變數(如有必要)。

dart
class Point {
  double x = 0;
  double y = 0;

  // Generative constructor with initializing formal parameters:
  Point(this.x, this.y);
}

this 關鍵字是指目前的執行個體。

初始化正式參數

#

Dart 具有初始化正式參數,可簡化將建構函式引數指定給執行個體變數的常見模式。直接在建構函式宣告中使用 this.propertyName,並省略主體。

初始化參數也允許您初始化不可為 Null 或 final 的執行個體變數,這兩個變數都必須初始化或提供預設值。

dart
class Point {
  final double x;
  final double y;

  // Sets the x and y instance variables
  // before the constructor body runs.
  Point(this.x, this.y);
}

初始化形式化引進的變數隱含為最終變數,且僅在 初始化清單 的範圍內。

如果您需要執行無法在初始化清單中表示的邏輯,請建立一個 工廠建構函式(或 靜態方法)包含該邏輯,然後將計算出的值傳遞給一般建構函式。

預設建構函式

#

如果您未宣告建構函式,系統會為您提供預設建構函式。預設建構函式沒有引數,且會呼叫超類別中的無引數建構函式。

建構函式不會繼承

#

子類別不會繼承其超類別的建構函式。未宣告任何建構函式的子類別只會有預設(無引數、無名稱)建構函式。

命名建構函式

#

使用命名建構函式來實作類別的多個建構函式或提供額外的說明。

dart
const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  final double x;
  final double y;

  // Sets the x and y instance variables
  // before the constructor body runs.
  Point(this.x, this.y);

  // Named constructor
  Point.origin()
      : x = xOrigin,
        y = yOrigin;
}

請記住,建構函式不會被繼承,這表示超類別的命名建構函式不會被子類別繼承。如果您希望子類別使用超類別中定義的命名建構函式建立,您必須在子類別中實作該建構函式。

呼叫非預設的超類別建構函式

#

預設情況下,子類別中的建構函式會呼叫超類別的未命名、無參數建構函式。超類別的建構函式會在建構函式主體的開頭呼叫。如果同時使用初始化清單,則會在呼叫超類別之前執行。總之,執行順序如下

  1. 初始化清單
  2. 超類別的無參數建構函式
  3. 主類別的無參數建構函式

如果超類別沒有未命名、無參數建構函式,則必須手動呼叫超類別中的其中一個建構函式。在冒號 (:) 後面指定超類別建構函式,就在建構函式主體(如果有)之前。

在以下範例中,Employee 類別的建構函式會呼叫其超類別 Person 的命名建構函式。按一下執行以執行程式碼。

class Person {
  String? firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson().
  Employee.fromJson(super.data) : super.fromJson() {
    print('in Employee');
  }
}

void main() {
  var employee = Employee.fromJson({});
  print(employee);
  // Prints:
  // in Person
  // in Employee
  // Instance of 'Employee'
}

由於會在呼叫建構函式之前評估傳遞給超類別建構函式的參數,因此參數可以是函式呼叫等運算式

dart
class Employee extends Person {
  Employee() : super.fromJson(fetchDefaultData());
  // ···
}

超參數

#

若要避免必須手動將每個參數傳遞到建構函式的超呼叫中,可以使用超初始化參數將參數轉發到指定的或預設超類別建構函式。此功能無法與重新導向建構函式搭配使用。超初始化參數的語法和語意與初始化形式參數類似

dart
class Vector2d {
  final double x;
  final double y;

  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;

  // Forward the x and y parameters to the default super constructor like:
  // Vector3d(final double x, final double y, this.z) : super(x, y);
  Vector3d(super.x, super.y, this.z);
}

如果超建構函式呼叫已經有位置參數,則超初始化參數不能是位置參數,但它們永遠可以命名

dart
class Vector2d {
  // ...

  Vector2d.named({required this.x, required this.y});
}

class Vector3d extends Vector2d {
  // ...

  // Forward the y parameter to the named super constructor like:
  // Vector3d.yzPlane({required double y, required this.z})
  //       : super.named(x: 0, y: y);
  Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
}

初始化清單

#

除了呼叫超類別建構函式之外,您還可以在建構函式主體執行之前初始化實例變數。使用逗號分隔初始化項。

dart
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, double> json)
    : x = json['x']!,
      y = json['y']! {
  print('In Point.fromJson(): ($x, $y)');
}

在開發期間,您可以使用初始化清單中的 assert 來驗證輸入。

dart
Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

在設定最終欄位時,初始化清單非常方便。下列範例在初始化清單中初始化三個最終欄位。按一下執行以執行程式碼。

import 'dart:math';

class Point {
  final double x;
  final double y;
  final double distanceFromOrigin;

  Point(double x, double y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

void main() {
  var p = Point(2, 3);
  print(p.distanceFromOrigin);
}

重新導向建構函式

#

有時候,建構函式的唯一目的就是重新導向到同一個類別中的另一個建構函式。重新導向建構函式的本體是空的,建構函式呼叫(使用 this 而不是類別名稱)出現在冒號 (:) 之後。

dart
class Point {
  double x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(double x) : this(x, 0);
}

常數建構函式

#

如果您的類別會產生永不變更的物件,您可以讓這些物件成為編譯時期常數。為執行此操作,請定義 const 建構函式,並確保所有執行個體變數都是 final

dart
class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

常數建構函式並非總是會建立常數。有關詳細資訊,請參閱 使用建構函式 一節。

工廠建構函式

#

在實作建構函式時,如果建構函式並非總是會建立其類別的新執行個體,請使用 factory 關鍵字。例如,工廠建構函式可能會從快取中傳回執行個體,或者可能會傳回子類型的執行個體。工廠建構函式的另一種使用案例是使用初始化清單中無法處理的邏輯來初始化最終變數。

在以下範例中,Logger 工廠建構函式會從快取傳回物件,而 Logger.fromJson 工廠建構函式會從 JSON 物件初始化最終變數。

dart
class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to
  // the _ in front of its name.
  static final Map<String, Logger> _cache = <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  factory Logger.fromJson(Map<String, Object> json) {
    return Logger(json['name'].toString());
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

呼叫工廠建構函式的方式與呼叫其他建構函式相同

dart
var logger = Logger('UI');
logger.log('Button clicked');

var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);