目录

类修饰符

:::版本说明 除了 abstract 之外,类修饰符至少需要 3.0 的 语言版本 。 :::

类修饰符控制类或混入的使用方式,包括 在其自身库中 的使用方式以及在其定义库外部的使用方式。

修饰符关键字位于类或混入声明之前。例如,编写 abstract class 定义了一个抽象类。可以在类声明前出现的完整修饰符集包括:

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

只有 base 修饰符可以出现在混入声明之前。修饰符不适用于其他声明,例如 enumtypedefextensionextension type

在决定是否使用类修饰符时,请考虑类的预期用途以及类需要依赖的行为。

:::注意 如果您维护一个库,请阅读 面向 API 维护者的类修饰符 页面,了解如何为您的库处理这些更改的指导。 :::

无修饰符

#

要允许从任何库无限制地构造或子类型化,请使用没有修饰符的 classmixin 声明。默认情况下,您可以:

  • 构造类的新的实例。
  • 扩展类以创建新的子类型。
  • 实现类或混入的接口。
  • 混入 混入或混入类。

abstract

#

要定义一个不需要对其整个接口进行完整、具体实现的类,请使用 abstract 修饰符。

抽象类不能从任何库(无论是其自身库还是外部库)构造。抽象类通常具有 抽象方法

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

// 错误:无法构造。
Vehicle myVehicle = Vehicle();

// 可以扩展。
class Car extends Vehicle {
  int passengers = 4;

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

// 可以实现。
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';

// 可以构造。
Vehicle myVehicle = Vehicle();

// 可以扩展。
base class Car extends Vehicle {
  int passengers = 4;
  // ...
}

// 错误:无法实现。
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';

// 可以构造。
Vehicle myVehicle = Vehicle();

// 错误:无法继承。
class Car extends Vehicle {
  int passengers = 4;
  // ...
}

// 可以实现。
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

abstract interface

#

interface 修饰符最常见的用途是定义纯接口。将 interfaceabstract 修饰符组合用于 abstract interface class

interface 类一样,其他库可以实现但不能继承纯接口。与 abstract 类一样,纯接口可以具有抽象成员。

final

#

要关闭类型层次结构,请使用 final 修饰符。这可以防止从当前库外部的类进行子类型化。禁止继承和实现完全禁止子类型化。这保证了:

  • 您可以安全地向 API 添加增量更改。
  • 您可以调用实例方法,同时知道它们没有在第三方子类中被覆盖。

最终类可以在同一库中扩展或实现。 final 修饰符包含 base 的效果,因此任何子类也必须标记为 basefinalsealed

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

// 可以构造。
Vehicle myVehicle = Vehicle();

// 错误:无法继承。
class Car extends Vehicle {
  int passengers = 4;
  // ...
}

class MockVehicle implements Vehicle {
  // 错误:无法实现。
  @override
  void moveForward(int meters) {
    // ...
  }
}

sealed

#

要创建已知的、可枚举的子类型集,请使用 sealed 修饰符。这允许您在这些子类型上创建一个 switch,该 switch 静态地保证是 详尽的

sealed 修饰符阻止从其自身库外部扩展或实现类。密封类隐式地是 抽象的

  • 它们本身不能被构造。
  • 它们可以具有 工厂构造函数
  • 它们可以为其子类定义要使用的构造函数。

但是,密封类的子类并非隐式抽象。

编译器知道任何可能的直接子类型,因为它们只能存在于同一个库中。这允许编译器在 switch 没有穷举地处理其所有情况中的所有可能的子类型时向您发出警告:

dart
sealed class Vehicle {}

class Car extends Vehicle {}

class Truck implements Vehicle {}

class Bicycle extends Vehicle {}

// 错误:无法实例化。
Vehicle myVehicle = Vehicle();

// 子类可以实例化。
Vehicle myCar = Car();

String getVehicleSound(Vehicle vehicle) {
  // 错误:switch 缺少 Bicycle 子类型或默认情况。
  return switch (vehicle) {
    Car() => 'vroom',
    Truck() => 'VROOOOMM',
  };
}

如果您不希望 详尽的切换 ,或者希望以后能够添加子类型而不会破坏 API,请使用 final 修饰符。有关更深入的比较,请阅读 sealedfinal

组合修饰符

#

您可以组合一些修饰符以进行分层限制。类声明可以按顺序为:

  1. (可选) abstract ,描述类是否可以包含抽象成员并阻止实例化。
  2. (可选) baseinterfacefinalsealed 之一,描述对其他库子类型化类的限制。
  3. (可选) mixin ,描述声明是否可以混入。
  4. class 关键字本身。

您不能组合某些修饰符,因为它们是矛盾的、冗余的或相互排斥的:

  • abstractsealedsealed 类是隐式 抽象的
  • interfacefinalsealedmixin 。这些访问修饰符阻止 混入

有关如何组合类修饰符的更多指导,请查看 类修饰符参考