Class modifiers reference(类修饰符参考)
类修饰符参考页适合在已经理解基本语义后快速查表。Dart 的类声明可以按固定顺序组合修饰符:先写可选的 abstract,再写可选的 base、interface、final 或 sealed 之一,再写可选的 mixin,最后写 class。
abstract base mixin class ReusableBase {}
除 abstract 外,类修饰符要求 Dart 语言版本至少为 3.0。下面的表格默认讨论“声明库之外”的能力;在声明类型的同一个库内部,限制通常更宽松。
能力速查表
| 声明 | 可构造 | 外部可 extends | 外部可 implements | 可 with 混入 | 支持穷尽检查 |
|---|---|---|---|---|---|
class | 是 | 是 | 是 | 否 | 否 |
base class | 是 | 是 | 否 | 否 | 否 |
interface class | 是 | 否 | 是 | 否 | 否 |
final class | 是 | 否 | 否 | 否 | 否 |
sealed class | 否 | 否 | 否 | 否 | 是 |
abstract class | 否 | 是 | 是 | 否 | 否 |
abstract base class | 否 | 是 | 否 | 否 | 否 |
abstract interface class | 否 | 否 | 是 | 否 | 否 |
abstract final class | 否 | 否 | 否 | 否 | 否 |
mixin class | 是 | 是 | 是 | 是 | 否 |
base mixin class | 是 | 是 | 否 | 是 | 否 |
abstract mixin class | 否 | 是 | 是 | 是 | 否 |
abstract base mixin class | 否 | 是 | 否 | 是 | 否 |
mixin | 否 | 否 | 是 | 是 | 否 |
base mixin | 否 | 否 | 否 | 是 | 否 |
“可构造”指能否直接创建该声明类型的实例;sealed class 隐式为抽象类,因此不能直接实例化。mixin 声明不是类声明,也不能被 extends,但它的接口可以被实现。
无修饰符的 class
普通 class 是最开放的声明方式。外部库可以构造、继承和实现它。
class Formatter {
String format(String value) => value.trim();
}
class UpperCaseFormatter extends Formatter {
String format(String value) => super.format(value).toUpperCase();
}
class CustomFormatter implements Formatter {
String format(String value) => '[$value]';
}
如果公开 API 没有明确限制,普通 class 给用户的自由最大,但维护者后续演进类型时也最容易受到外部子类和实现类影响。
abstract 组合
abstract 只控制能否实例化,不直接控制外部库能否继承或实现。
abstract class Parser {
Object parse(String source);
}
final class JsonParser extends Parser {
Object parse(String source) => {'source': source};
}
abstract 可以和 base、interface、final、mixin class 组合:
| 声明 | 主要含义 |
|---|---|
abstract base class | 不能构造;外部只能继承实现,不能实现接口 |
abstract interface class | 不能构造;外部只能实现接口,不能继承实现 |
abstract final class | 不能构造;外部不能继承或实现 |
abstract mixin class | 不能构造;可作为类继承,也可作为 Mixin 混入 |
abstract base mixin class | 不能构造;可继承和混入,但外部不能实现 |
abstract interface class 是表达纯接口的常用写法;abstract final class 常用于只包含静态成员的工具类型。
base 与传递性
base 的重点是保留实现继承。外部库可以 extends,不能 implements。
base class Repository {
int _revision = 0;
void save() {
_revision++;
}
int get revision => _revision;
}
final class UserRepository extends Repository {}
直接继承或实现 base 类型的类必须声明为 base、final 或 sealed。这能把“外部不能绕过实现”的约束继续传下去。
base mixin 和 base mixin class 也保留类似意图:外部使用者可以复用实现,但不能只实现接口来绕开实现。
interface 与纯接口
interface class 允许外部实现,禁止外部继承。
interface class Clock {
DateTime now() => DateTime.now();
}
final class FixedClock implements Clock {
FixedClock(this.value);
final DateTime value;
DateTime now() => value;
}
如果这个类型本来就不应该被构造,写成 abstract interface class 更准确:
abstract interface class Serializer<T> {
String serialize(T value);
}
interface 适合把“契约”和“实现复用”分开。API 用户可以提供替代实现,但不能继承你的方法体。
final 与 sealed
final class 和 sealed class 都禁止外部创建子类型,但它们解决的问题不同。
final class UserId {
const UserId(this.value);
final String value;
}
final class 可以构造,不支持穷尽检查。它适合不希望外部扩展的普通公开类型。
sealed class AuthResult {}
final class Authenticated extends AuthResult {
Authenticated(this.userId);
final UserId userId;
}
final class Rejected extends AuthResult {
Rejected(this.reason);
final String reason;
}
sealed class 不能构造,直接子类型必须在同一个库中声明。分析器可以据此检查 switch 是否覆盖所有直接分支。
合法组合的写法顺序
修饰符顺序不是任意的。合法声明遵循下面顺序:
abstract base mixin class CacheBehavior {}
可理解为:
abstract:这个类型是否能直接实例化。base、interface、final、sealed:外部库能否继承或实现。mixin:这个声明是否也能出现在with子句中。class:声明类。
普通 mixin 声明不是 class,写法如下:
base mixin Identifiable {
String get id;
}
非法或无意义的组合
有些修饰符不能组合,因为它们表达的限制互相冲突或重复。
| 非法组合 | 原因 |
|---|---|
base、interface、final 彼此组合 | 它们都控制外部 extends 和 implements 能力,策略互斥 |
sealed abstract class | sealed 已经隐式不可构造,和 abstract 重复 |
sealed base class、sealed interface class、sealed final class | sealed 已经禁止外部继承和实现,组合后限制重复 |
interface mixin class、final mixin class、sealed mixin class | 这些修饰符会禁止混入,和 mixin class 的目的冲突 |
abstract mixin | mixin 本身不能构造,和 abstract 重复 |
enum 加类修饰符 | enum 不是普通类声明,不能用这些修饰符调整继承能力 |
extension type 加类修饰符 | extension type 有自己的实现和类型规则,不使用类修饰符 |
记忆时可以先问两个问题:这个声明能否构造;外部库能否创建子类型。abstract 和 sealed 影响构造能力,base、interface、final、sealed 影响外部子类型能力,mixin 影响能否通过 with 复用实现。
TypeScript 对比
TypeScript 没有和 Dart 类修饰符一一对应的外部库边界控制。abstract class 可以类比 Dart 的 abstract class,但 TypeScript 的 interface 不会像 Dart 的 interface class 一样控制继承实现。
abstract class Parser {
abstract parse(source: string): unknown
}
interface Serializer<T> {
serialize(value: T): string
}
Dart 的差异在于:类本身就能作为接口被 implements,而类修饰符进一步规定外部库能否 extends、implements 或 with。因此学习 Dart 时,应优先记住 Dart 的库边界和子类型规则,再把 TypeScript 当作辅助类比。