概况与用法
版本提示!
除 abstract 修饰符外,所有其他类修饰符均要求 Dart 语言版本不低于 3.0。
类修饰符用于控制类或混入的使用方式,无论是在其自身库内部,还是在定义该库的外部。
修饰符关键字必须位于类或混入声明之前。例如,使用 abstract class 可以定义一个抽象类。类声明前可用的完整修饰符集合包括:
- abstract
- base
- final
- interface
- sealed
- mixin
只有 base 修饰符可以出现在混入 (mixin) 声明之前。这些修饰符不适用于其他声明,如 enum 、typedef 、extension 或 extension type。
在决定是否使用类修饰符时,应考虑该类的预期用途及其需要依赖的行为。
提示!
若您维护代码库,请查阅《API维护者类修饰符指南》,了解如何为您的库适配此变更。
无修饰符
若需允许从任意代码库无限制地实例化或继承,请使用无修饰符的 class 或 mixin 声明。默认情况下,您可以:
- 实例化类
- 通过继承创建子类
- 实现类或混入的接口
- 混入mixin或mixin类
abstract
要定义一个无需完整实现全部接口的类,请使用 abstract 修饰符。
抽象类不可被任何库实例化(无论其定义库还是外部库),且通常包含抽象方法。
a.dart:
/// Vehicle 是一个抽象类
abstract class Vehicle {void moveForward(int meters); // 抽象方法
}
b.dart:
import 'a.dart';// 错误:不可实例化
Vehicle myVehicle = Vehicle();// 可被继承
class Car extends Vehicle {int passengers = 4;@overridevoid moveForward(int meters) {// ...}
}// 可被实现
class MockVehicle implements Vehicle {@overridevoid moveForward(int meters) {// ...}
}
若需使抽象类具备可实例化的特性,请定义工厂构造函数。
abstract class Vehicle {void moveForward(int meters); // 抽象方法factory Vehicle() => return Car(); // 工厂函数(实例化实现类)
}// 可被实现(Vehicle抽象类里有工厂构造函数时不能被继承)
class Car implements Vehicle {int passengers = 4;@overridevoid moveForward(int meters) => print("这是一辆$meters米长的小车");
}void main() {Vehicle myVehicle = Vehicle(); // myVehicle 实际上是一个 Car 类型myVehicle.moveForward(4);
}
base
为强制要求类或混入 (mixin) 必须通过继承实现,请使用 base 修饰符。基类禁止在定义库之外被实现,这保证了:
- 创建子类型实例时必定调用基类构造函数。
- 所有已实现的私有成员都在子类型中存在。
- base 类新增成员不会破坏子类型(子类型自动继承新成员)。
- 除非子类型已存在同名且签名不兼容的成员。
任何实现或继承基类的类,都必须被标记为 base 、final 或 sealed。这一机制可防止外部库破坏基类所确保的各项特性。
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 class MockVehicle implements Vehicle {@overridevoid moveForward() {// ...}
}
interface
要定义一个接口,需使用 interface 修饰符。接口所在库之外的库可以实现该接口,但不能继承它。这确保了:
- 当类的实例方法调用 this 上的另一个实例方法时,它始终会执行来自同一库的已知方法实现。
- 其他库无法以意外的方式重写接口类自身方法后续可能调用的方法,从而减少脆弱的基类问题。
a.dart:
interface class Vehicle {void moveForward(int meters) {// ...}
}
b.dart:
import 'a.dart';// 可被实例化
Vehicle myVehicle = Vehicle();// 错误:不可继承
class Car extends Vehicle {int passengers = 4;// ...
}// 可被实现
class MockVehicle implements Vehicle {@overridevoid moveForward(int meters) {// ...}
}
注意!
前面章节 “类与对象” 中的隐式接口小节介绍过,每个类都隐式定义了一个接口。接口所在库之外的库可以实现和继承该隐式接口。但使用 interface 修饰符的显式接口只能实现而不能继承该显式接口(如果同在一个库内则可以继承)。
abstract interface
使用 interface 修饰符最常见的场景是定义纯接口。将 interface 和 abstract 修饰符结合使用可创建 abstract interface class。
与 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();// 错误:不可继承
class Car extends Vehicle {int passengers = 4;// ...
}// 错误:无法实现
class MockVehicle implements Vehicle {@overridevoid moveForward(int meters) {// ...}
}
sealed
要创建一个已知的、可枚举的子类型集合,请使用 sealed 修饰符。这允许您创建一个针对这些子类型的 switch 语句,且能静态确保覆盖所有可能情况。
sealed 修饰符会阻止类在自身所属库之外被继承或实现。sealed (密封)类默认是抽象的。
- 它们自身不能被实例化。
- 可以包含工厂构造函数。
- 可以为它们的子类定义构造函数。
不过,密封类的子类默认不是抽象的。
编译器能够识别所有可能的直接子类型,因为这些子类型只能存在于同一库中。这一机制使得编译器可以在switch语句未能穷尽处理所有可能的子类型时发出警告:
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子类型的处理或default casereturn switch (vehicle) {Car() => 'vroom',Truck() => 'VROOOOMM',};
}
若不需要穷尽性匹配,或希望后续能添加子类型而不破坏API兼容性,请使用 final 修饰符。如需更深入的比较,请阅读 sealed 与 final 的对比。
修饰符组合
您可以通过组合修饰符来实现多层次的限制。类声明按顺序可以包含以下元素:
1、(可选)abstract :决定类是否可以包含抽象成员并阻止实例化。
2、(可选)base 、interface 、final 或 sealed 之一:限制其他库对该类的子类型化。
3、(可选)mixin :决定该声明是否可以被混入。
4、 class 关键字本身。
某些修饰符不能组合使用,因为它们存在矛盾、冗余或互斥关系:
- abstract 与 sealed:密封类默认就是抽象的。
- interface 、final 或 sealed 与 mixin:这些访问修饰符会阻止混入。
API维护者类修饰符指南
详情 >
类修饰符参考指南
详情 >