跳到主內容

類修飾符

類修飾符控制類或混入(mixin)如何使用,包括 在其自身庫中 和在其定義庫之外。

修飾符關鍵字位於類或混入宣告之前。例如,編寫 abstract class 定義了一個抽象類。可以出現在類宣告之前的完整修飾符集包括

  • abstract
  • base
  • final
  • interface
  • sealed
  • mixin

只有 base 修飾符可以出現在混入宣告之前。這些修飾符不適用於其他宣告,如 enumtypedefextensionextension type

在決定是否使用類修飾符時,請考慮類的預期用途以及該類需要依賴的行為。

無修飾符

#

要允許從任何庫無限制地構造或建立子型別,請使用不帶修飾符的 classmixin 宣告。預設情況下,您可以

abstract

#

要定義一個不需要完整、具體實現其整個介面的類,請使用 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;

  @override
  void moveForward(int meters) {
    // ...
  }
}

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

如果您希望抽象類看起來是可例項化的,請定義一個 工廠建構函式

base

#

要強制繼承類或混入的實現,請使用 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

#

要定義介面,請使用 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) {
    // ...
  }
}

abstract interface

#

interface 修飾符最常見的用途是定義一個純介面。組合 interfaceabstract 修飾符可用於 abstract interface class

interface 類類似,其他庫可以實現純介面,但不能繼承它。與 abstract 類類似,純介面可以有抽象成員。

final

#

要關閉型別層次結構,請使用 final 修飾符。這可以防止從當前庫之外的類進行子型別化。禁止繼承和實現可完全阻止子型別化。這保證了

  • 您可以安全地向 API 新增增量更改。
  • 您可以呼叫例項方法,因為您知道它們尚未在第三方子類中被覆蓋。

Final 類可以在同一庫中進行擴充套件或實現。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 修飾符。這允許您對這些子型別進行切換,並靜態確保其 窮盡性

sealed 修飾符可防止類在其自身庫之外被擴充套件或實現。密封類隱式地是 抽象的

  • 它們本身不能被構造。
  • 它們可以有 工廠建構函式
  • 它們可以為子類定義建構函式供其使用。

然而,密封類的子類並不隱式地是抽象的。

編譯器知道所有可能的直接子型別,因為它們只能存在於同一個庫中。這使得編譯器可以在 switch 語句未能窮盡處理其所有可能子型別的情況下提醒您

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。一個 密封 類隱式地是 抽象的
  • interfacefinalsealedmixin。這些訪問修飾符會阻止 混入

有關如何組合類修飾符的進一步指導,請查閱類修飾符參考