跳到主要内容

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 {}

可理解为:

  1. abstract:这个类型是否能直接实例化。
  2. base、interface、final、sealed:外部库能否继承或实现。
  3. mixin:这个声明是否也能出现在 with 子句中。
  4. class:声明类。

普通 mixin 声明不是 class,写法如下:

base mixin Identifiable {
String get id;
}

非法或无意义的组合​

有些修饰符不能组合,因为它们表达的限制互相冲突或重复。

非法组合原因
base、interface、final 彼此组合它们都控制外部 extends 和 implements 能力,策略互斥
sealed abstract classsealed 已经隐式不可构造,和 abstract 重复
sealed base class、sealed interface class、sealed final classsealed 已经禁止外部继承和实现,组合后限制重复
interface mixin class、final mixin class、sealed mixin class这些修饰符会禁止混入,和 mixin class 的目的冲突
abstract mixinmixin 本身不能构造,和 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 当作辅助类比。