在写 Flutter 项目的时候,经常会瞄到 sealed class 的类,但是当时也就把它们当常规类,没有进一步了解,恰巧今天早上又遇到,索性深入研究一番。
对于这种概念性问题,一般都是从第一手资料出发,直接前往官网:
密封类是 Dart 3.0 引入的重要特性,通过限制类的继承范围和提供编译时检查,显著提升了代码的健壮性和可维护性。在 Flutter 开发中,它尤其适用于状态管理、错误处理等需要明确类型分支的场景。合理使用密封类,可以避免因类型遗漏导致的运行时错误,同时使代码逻辑更加清晰。
我们简单归纳下核心特性:
-
子类可枚举性
密封类的所有直接子类必须定义在同一库(同一文件或包)中,编译器能明确知道其所有可能的子类型。例如:
sealed class AuthState {} // 密封类 class AuthLoading extends AuthState {} // 子类 class AuthSuccess extends AuthState {} // 子类 -
隐式抽象性
密封类自动是抽象类,无法被实例化,且子类必须显式继承或实现其定义。
-
限制外部扩展
密封类禁止外部库继承或实现,确保类型层次结构的封闭性。例如:
// 外部库无法执行以下操作: class ExternalAuthState extends AuthState {} // 编译错误 -
与 switch 语句配合
在 switch 中匹配密封类时,编译器会强制检查是否覆盖所有子类,避免遗漏分支:
String buildUI(AuthState state) { return switch (state) { AuthLoading() => '加载中...', AuthSuccess() => '成功', AuthFailure() => '失败', // 编译器提示缺少其他子类分支 }; }
与类似概念的对比
就对类声明关键字上,我们可以对比下类似概念的差异
| 特性 | sealed | final | abstract |
|---|---|---|---|
| 继承限制 | 禁止外部继承和实现 | 禁止外部继承 | 允许继承,但不可实例化 |
| 子类可见性 | 子类必须同库 | 无限制 | 无限制 |
| 用途 | 有限状态集合 | 关闭继承链 | 定义抽象接口或部分实现 |
典型应用场景
-
状态管理
在 Flutter 的状态管理(如 BLoC、Bloc)中,密封类常用于定义有限的 UI 状态或事件类型,确保所有分支被处理:sealed class CounterEvent {} class Increment implements CounterEvent {} class Decrement implements CounterEvent {} class CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { on<Increment>(_onIncrement); on<Decrement>(_onDecrement); } } -
API 设计
通过密封类限制外部对某个类的扩展,例如定义一组固定的错误类型:sealed class ApiError {} class NetworkError extends ApiError {} class ServerError extends ApiError {} -
类型安全的模式匹配 结合 Dart 的
switch表达式,密封类能提供类似 Kotlin 的模式匹配能力,增强代码可读性。