类修饰符(Class modifiers)
语言版本要求
除了 abstract 之外,其他类修饰符都需要至少 Dart 3.0 的语言版本。
类修饰符用于控制一个类或混入(mixin)在其自身库内部以及外部库中的使用方式。
修饰符关键字位于类或混入声明之前。例如,abstract class 表示定义一个抽象类。在类声明前可以使用的完整修饰符集合包括:
- abstract
- base
- final
- interface
- sealed
- mixin
只有 base 修饰符可以出现在混入(mixin)声明之前。这些修饰符不适用于 enum、typedef、extension 或 extension type 等其他声明类型。
在决定是否使用类修饰符时,请考虑该类的预期用途,以及该类需要依赖哪些行为。
提示
如果您已经熟悉 Dart 的类修饰符,只是想快速回顾它们组合使用时的行为,请查阅《类修饰符参考》。
如果您维护一个库,请阅读《面向 API 维护者的类修饰符指南》,了解如何在您的库中应用这些变更。
无修饰符
若希望允许任何库无限制地构造实例或创建子类型,请使用不带任何修饰符的类或混入声明。默认情况下,您可以:
- 构造该类的新实例。
- 继承该类以创建新的子类型。
- 实现该类或混入的接口。
- 混入(mix in)一个混入或混入类(mixin class)。
abstract(抽象)
若要定义一个不需要完整具体实现其全部接口的类,请使用 abstract 修饰符。
抽象类无法从任何库(包括其自身库或外部库)中被实例化。抽象类通常包含抽象方法。
a.dart
abstract class Vehicle {
void moveForward(int meters);
}b.dart
import 'a.dart';
// 错误:`Vehicle` 无法被实例化,
// 因为它被标记为 `abstract`。
Vehicle myVehicle = Vehicle();
// 可以被继承。
class Car extends Vehicle {
int passengers = 4;
@override
void moveForward(int meters) {
// ...
}
}
// 可以被实现。
class MockVehicle implements Vehicle {
@override
void moveForward(int meters) {
// ...
}
}如果您希望抽象类看起来可以被实例化,请为其定义一个工厂构造函数(factory constructor)。
base(基类)
若要强制继承类或混入的具体实现,请使用 base 修饰符。base 类禁止在其定义库之外被实现。这保证了:
- 每当创建该类的子类型实例时,一定会调用 base 类的构造函数。
- 所有已实现的私有成员在子类型中都存在。
- 在 base 类中新增一个成员不会破坏子类型,因为所有子类型都会继承这个新成员(除非子类型已声明了同名但签名不兼容的成员)。
任何实现或继承 base 类的类,必须标记为 base、final 或 sealed。这可以防止外部库破坏 base 类的上述保证。
a.dart
base class Vehicle {
void moveForward(int meters) {
// ...
}
}b.dart
import 'a.dart';
// 可以被构造。
Vehicle myVehicle = Vehicle();
// 可以被继承。
base class Car extends Vehicle {
int passengers = 4;
// ...
}
// 错误:`Vehicle` 不能在不同库中被实现,
// 因为它被标记为 `base`。
base class MockVehicle implements Vehicle {
@override
void moveForward() {
// ...
}
}interface(接口)
若要定义一个接口,请使用 interface 修饰符。在接口定义库之外的库可以实现该接口,但不能继承它。这保证了:
- 当类中的某个实例方法调用 this 上的另一个实例方法时,始终会调用同一库中已知的实现。
- 其他库无法以意外方式覆盖接口类方法可能调用的方法,从而减少“脆弱基类问题”(fragile base class problem)。
a.dart
interface class Vehicle {
void moveForward(int meters) {
// ...
}
}b.dart
import 'a.dart';
// 可以被构造。
Vehicle myVehicle = Vehicle();
// 错误:`Vehicle` 不能在不同库中被继承,
// 因为它被标记为 `interface`。
class Car extends Vehicle {
int passengers = 4;
// ...
}
// 可以被实现。
class MockVehicle implements Vehicle {
@override
void moveForward(int meters) {
// ...
}
}abstract interface(抽象接口)
interface 修饰符最常见的用途是定义纯接口。将 interface 与 abstract 修饰符结合,即可定义一个抽象接口类。
与接口类一样,其他库可以实现但不能继承纯接口;与抽象类一样,纯接口可以包含抽象成员。
final(最终类)
若要封闭类型层级结构,请使用 final 修饰符。这会阻止在当前库之外对该类进行子类型化。禁止继承和实现可以完全防止子类型化。这保证了:
- 您可以安全地对 API 进行增量变更。
- 调用实例方法时,可以确信这些方法不会被第三方子类覆盖。
final 类可以在同一库内被继承或实现。final 修饰符包含 base 的效果,因此其任何子类也必须标记为 base、final 或 sealed。
a.dart
final class Vehicle {
void moveForward(int meters) {
// ...
}
}b.dart
import 'a.dart';
// 可以被构造。
Vehicle myVehicle = Vehicle();
// 错误:`Vehicle` 不能在不同库中被继承,
// 因为它被标记为 `final`。
class Car extends Vehicle {
int passengers = 4;
// ...
}
// 错误:`Vehicle` 不能在不同库中被实现,
// 因为它被标记为 `final`。
class MockVehicle implements Vehicle {
@override
void moveForward(int meters) {
// ...
}
}sealed(密封类)
若要创建一组已知且可枚举的子类型,请使用 sealed 修饰符。这允许您编写一个 switch 表达式,并由编译器静态确保其覆盖所有可能的子类型。
sealed 修饰符禁止在定义库之外对该类进行继承或实现。密封类是隐式抽象的:
- 它们自身不能被实例化。
- 它们可以拥有工厂构造函数。
- 它们可以定义供子类使用的构造函数。
但密封类的子类本身不是隐式抽象的。
由于所有直接子类型只能存在于同一库中,编译器能够识别所有可能的子类型。因此,当 switch 表达式未穷尽处理所有子类型时,编译器会发出警告:
sealed class Vehicle {}
class Car extends Vehicle {}
class Truck implements Vehicle {}
class Bicycle extends Vehicle {}
// 错误:`Vehicle` 无法被实例化,
// 因为它被标记为 `sealed`,因此隐式抽象。
Vehicle myVehicle = Vehicle();
// 密封类的子类可以被实例化(除非另有约束)。
Vehicle myCar = Car();
extension VehicleSounds on Vehicle {
String get sound {
// 错误:该 switch 未穷尽处理所有 `Vehicle` 类型的可能对象。
// 例如,运行时类型为 `Bicycle` 的 `Vehicle` 对象
// 将无法匹配任何 case。
return switch (this) {
Car() => 'vroom',
Truck() => 'VROOOOMM',
};
}
}如果您不需要穷尽式 switch,或者希望日后能添加新子类型而不破坏 API,请使用 final 修饰符。更详细的对比请参阅《sealed 与 final 的区别》。
组合修饰符
某些修饰符可以组合使用,以实现多层限制。类声明的修饰符顺序如下(按出现顺序):
- (可选)abstract:表示该类是否可以包含抽象成员,并禁止实例化。
- (可选)base、interface、final 或 sealed 中的一个:表示对其他库子类型化的限制。
- (可选)mixin:表示该声明是否可以被混入。
- class 关键字本身。
某些修饰符不能组合使用,因为它们相互矛盾、冗余或互斥:
- abstract 与 sealed:sealed 类已经是隐式 abstract。
- interface、final 或 sealed 与 mixin:这些访问修饰符禁止混入。
如需进一步了解类修饰符的组合方式,请查阅《类修饰符参考》。