掌握 Dart 的 sealed class(一)

368 阅读2分钟

在写 Flutter 项目的时候,经常会瞄到 sealed class 的类,但是当时也就把它们当常规类,没有进一步了解,恰巧今天早上又遇到,索性深入研究一番。

对于这种概念性问题,一般都是从第一手资料出发,直接前往官网:

👉 dart.dev/language/cl…

密封类是 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() => '失败',
        // 编译器提示缺少其他子类分支
      };
    }
    

与类似概念的对比

就对类声明关键字上,我们可以对比下类似概念的差异

特性sealedfinalabstract
继承限制禁止外部继承和实现禁止外部继承允许继承,但不可实例化
子类可见性子类必须同库无限制无限制
用途有限状态集合关闭继承链定义抽象接口或部分实现

典型应用场景

  1. 状态管理
    在 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);
      }
    }
    
  2. API 设计
    通过密封类限制外部对某个类的扩展,例如定义一组固定的错误类型:

    sealed class ApiError {}
    class NetworkError extends ApiError {}
    class ServerError extends ApiError {}
    
  3. 类型安全的模式匹配 结合 Dart 的 switch 表达式,密封类能提供类似 Kotlin 的模式匹配能力,增强代码可读性。