內容

類別修飾詞

類別修飾詞控制類別或混合如何使用,從其 自己的函式庫內部 和定義它的函式庫外部。

修飾詞關鍵字出現在類別或混合宣告之前。例如,撰寫 abstract class 定義抽象類別。出現在類別宣告之前的完整修飾詞組包括

  • 抽象
  • 基礎
  • 最後
  • 介面
  • 密封
  • mixin

只有 base 修飾詞可以在 mixin 宣告之前出現。這些修飾詞不套用於其他宣告,例如 enumtypedefextensionextension type

在決定是否使用類別修飾詞時,請考慮類別預期的用途,以及類別需要能夠依賴哪些行為。

沒有修飾詞

#

若要允許不受限制地從任何函式庫建構或建立子類型,請使用沒有修飾詞的 classmixin 宣告。預設情況下,您可以

  • 建構類別的新執行個體。
  • 延伸類別以建立新的子類型。
  • 實作類別或 mixin 的介面。
  • 混入mixin 或 mixin 類別。

抽象

#

若要定義不需要完整具體實作其整個介面的類別,請使用 abstract 修飾詞。

抽象類別無法從任何函式庫建構,無論是其自己的函式庫還是外部函式庫。抽象類別通常有 抽象方法

a.dart
dart
abstract class Vehicle {
  void moveForward(int meters);
}
b.dart
dart
import 'a.dart';

// Error: Can't be constructed.
Vehicle myVehicle = Vehicle();

// Can be extended.
class Car extends Vehicle {
  int passengers = 4;
  // ···
}

// Can be implemented.
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

如果您希望抽象類別看起來可以實例化,請定義 工廠建構函式

基礎

#

若要強制執行類別或 mixin 實作的繼承,請使用 base 修飾詞。基本類別不允許在自己的函式庫之外實作。這保證

  • 每當建立類別子類型的執行個體時,都會呼叫基本類別建構函式。
  • 所有已實作的私有成員都存在於子類型中。
  • base 類別中的新實作成員不會中斷子類型,因為所有子類型都會繼承新成員。
    • 除非子類型已經宣告具有相同名稱和不相容簽章的成員,否則為真。

您必須將實作或延伸基本類別的任何類別標記為 basefinalsealed。這可防止外部函式庫中斷基本類別保證。

a.dart
dart
base class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}
b.dart
dart
import 'a.dart';

// Can be constructed.
Vehicle myVehicle = Vehicle();

// Can be extended.
base class Car extends Vehicle {
  int passengers = 4;
  // ...
}

// ERROR: Can't be implemented.
base class MockVehicle implements Vehicle {
  @override
  void moveForward() {
    // ...
  }
}

介面

#

若要定義介面,請使用 interface 修飾詞。介面定義函式庫外部的函式庫可以實作介面,但不能延伸介面。這保證

  • 當類別的其中一個執行個體方法在 this 上呼叫另一個執行個體方法時,它將始終從同一個函式庫呼叫該方法的已知實作。
  • 其他函式庫無法覆寫介面類別自己的方法,而這些方法可能稍後會以意外的方式呼叫。這會減少脆弱基底類別問題
a.dart
dart
interface class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}
b.dart
dart
import 'a.dart';

// Can be constructed.
Vehicle myVehicle = Vehicle();

// ERROR: Can't be inherited.
class Car extends Vehicle {
  int passengers = 4;
  // ...
}

// Can be implemented.
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

抽象介面

#

interface 修飾詞最常見的用途是定義純介面。結合 interfaceabstract 修飾詞,以建立一個 abstract interface class

interface 類別一樣,其他函式庫可以實作純介面,但無法繼承。與 abstract 類別一樣,純介面可以有抽象成員。

最後

#

若要關閉類型階層,請使用 final 修飾詞。這會防止當前函式庫外部的類別進行子類型化。禁止繼承和實作會完全防止子類型化。這會保證

  • 您可以安全地對 API 進行增量變更。
  • 您可以呼叫實例方法,因為您知道它們不會在第三方子類別中被覆寫。

最終類別可以在同一個函式庫中進行延伸或實作。final 修飾詞包含 base 的效果,因此任何子類別也必須標記為 basefinalsealed

a.dart
dart
final class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}
b.dart
dart
import 'a.dart';

// Can be constructed.
Vehicle myVehicle = Vehicle();

// ERROR: Can't be inherited.
class Car extends Vehicle {
  int passengers = 4;
  // ...
}

class MockVehicle implements Vehicle {
  // ERROR: Can't be implemented.
  @override
  void moveForward(int meters) {
    // ...
  }
}

密封

#

若要建立已知且可列舉的子類型組,請使用 sealed 修飾詞。這允許您建立一個在這些子類型上進行切換,且靜態確保為窮舉

sealed 修飾詞會防止類別在自己的函式庫外部進行延伸或實作。密封類別會隱含地為抽象

  • 它們無法自行建構。
  • 它們可以有工廠建構函式
  • 它們可以定義子類別要使用的建構函式。

但是,密封類別的子類別不會隱含地為抽象。

編譯器知道任何可能的直接子類型,因為它們只能存在於同一個函式庫中。這允許編譯器在切換時,如果其案例並未窮舉處理所有可能的子類型,就會提醒您

dart
sealed class Vehicle {}

class Car extends Vehicle {}

class Truck implements Vehicle {}

class Bicycle extends Vehicle {}

// ERROR: Can't be instantiated.
Vehicle myVehicle = Vehicle();

// Subclasses can be instantiated.
Vehicle myCar = Car();

String getVehicleSound(Vehicle vehicle) {
  // ERROR: The switch is missing the Bicycle subtype or a default case.
  return switch (vehicle) {
    Car() => 'vroom',
    Truck() => 'VROOOOMM',
  };
}

如果您不想要窮舉切換,或想要能夠在不中斷 API 的情況下稍後新增子類型,請使用 final 修飾詞。若要深入比較,請閱讀 sealedfinal

組合修飾詞

#

您可以結合一些修飾詞,以進行分層限制。類別宣告可以按順序為

  1. (選用) abstract,描述類別是否可以包含抽象成員,並防止實例化。
  2. (選用) baseinterfacefinalsealed 其中之一,描述對其他函式庫對類別進行子類型化的限制。
  3. (選用)mixin,描述宣告是否可以混合使用。
  4. class 關鍵字本身。

您無法組合某些修飾詞,因為它們相互矛盾、多餘或互斥

  • abstractsealed。一個 sealed 類別隱含地是 abstract
  • interfacefinalsealedmixin。這些存取修飾詞會阻止 混合使用

如需有關如何組合類別修飾詞的進一步說明,請查看 類別修飾詞參考