跳到主要内容

Class modifiers reference(类修饰符参考)

类修饰符参考页适合在已经理解基本语义后快速查表。Dart 的类声明可以按固定顺序组合修饰符:先写可选的 abstract,再写可选的 baseinterfacefinalsealed 之一,再写可选的 mixin,最后写 class

abstract base mixin class ReusableBase {}

abstract 外,类修饰符要求 Dart 语言版本至少为 3.0。下面的表格默认讨论“声明库之外”的能力;在声明类型的同一个库内部,限制通常更宽松。

能力速查表

声明可构造外部可 extends外部可 implementswith 混入支持穷尽检查
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 可以和 baseinterfacefinalmixin 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 类型的类必须声明为 basefinalsealed。这能把“外部不能绕过实现”的约束继续传下去。

base mixinbase 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 用户可以提供替代实现,但不能继承你的方法体。

finalsealed

final classsealed 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. baseinterfacefinalsealed:外部库能否继承或实现。
  3. mixin:这个声明是否也能出现在 with 子句中。
  4. class:声明类。

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

base mixin Identifiable {
String get id;
}

非法或无意义的组合

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

非法组合原因
baseinterfacefinal 彼此组合它们都控制外部 extendsimplements 能力,策略互斥
sealed abstract classsealed 已经隐式不可构造,和 abstract 重复
sealed base classsealed interface classsealed final classsealed 已经禁止外部继承和实现,组合后限制重复
interface mixin classfinal mixin classsealed mixin class这些修饰符会禁止混入,和 mixin class 的目的冲突
abstract mixinmixin 本身不能构造,和 abstract 重复
enum 加类修饰符enum 不是普通类声明,不能用这些修饰符调整继承能力
extension type 加类修饰符extension type 有自己的实现和类型规则,不使用类修饰符

记忆时可以先问两个问题:这个声明能否构造;外部库能否创建子类型。abstractsealed 影响构造能力,baseinterfacefinalsealed 影响外部子类型能力,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,而类修饰符进一步规定外部库能否 extendsimplementswith。因此学习 Dart 时,应优先记住 Dart 的库边界和子类型规则,再把 TypeScript 当作辅助类比。