扩展类型

扩展类型(Extension types)是一种编译时抽象(仅存在编译时期),是对现有类型的一种静态包装。它们是静态 JS 互操作(static JS interop)的重要组件,因为它们可以轻松修改现有类型的接口,而无需产生包装类的成本。

扩展类型对底层类型(称为表示类型,representation type)对象可用的操作集(或接口)强制进行约束。定义扩展类型时,可以复用表示类型的某些成员、省略/替换其它成员、以及添加新功能,这有点类似于设计模式里的外观模式

声明

使用 extension type 关键字声明扩展类型。

// ex561.dart
extension type IdNumber(int i) {} // 1

void main() {
  var id = IdNumber(123); // 2
  print(id); // 3 Output: 123
  print(id.i); // 3a Output: 123
  print(id.runtimeType); // 4 Output: int
}
  1. 声明扩展类型 IdNumber, 其表示类型为 int,同时也隐式地定义了
  • 构造函数 IdNumber(this.i)
  • 实例变量 final int i
  1. 声明变量 id,赋值为IdNumber(123)
  2. 分别打印 idid.i
  3. 打印 id的运行时类型,结果为 int,这印证了IdNumber在编译过程中被抹掉了。

构造函数

命名构造函数

除了隐式的构造函数外,还可以为扩展类型定义命名构造函数。

// ex562.dart
extension type IdNumber(int i) {
  IdNumber.of(String str) : i = int.parse(str);
}

void main() {
  var x = IdNumber(123);
  var y = IdNumber.of('123');
  assert(x == y);
}

采用命名构造函数的形式声明

使用命名构造函数的形式声明扩展类型时,可以灵活的定义不具名构函数(可选的)。

// ex563.dart
extension type IdNumber.of(int i) {
  IdNumber(String str) : i = int.parse(str);
}

void main() {
  var x = IdNumber.of(123);
  var y = IdNumber('123');
  assert(x == y);
}

对外隐藏不具名构造函数

// ex564.dart
extension type const IdNumber._(int i) {
  const IdNumber.of(int i) : this._(i);
}

此例也演示了扩展类型如何声明 const 构造函数。

工厂构造函数

// ex565.dart
extension type IdNumber._(int i) {
  factory IdNumber.of(int i) => IdNumber._(i);
}

成员

扩展类型不能定义 non-external 实例字段和抽象成员,除此之外可定义下列成员:

  • 方法
  • getter
  • setter
  • 操作符(operators)
// ex566.dart
extension type N(int i) {
  N operator +(N m) => N(i + m.i);
  int get n => i;
  bool isPositive() => i > 0;
}

void main() {
  var n = N(100);
  print(n + N(23)); // Output: 123
  print(n.n); // Output: 100
  print(n.isPositive()); // Output: true
}

实现接口

扩展类型只可实现:

实现表示类型

一个扩展类型实现它的表示类型时,它便自动拥有了表示类型的所有的成员。

// ex567.dart
import 'package:meta/meta.dart';

extension type N(int i) implements int {
  bool get isPositive => i > 0;

  @redeclare
  N operator +(int n) => N(i + n);
}

void main() {
  var a = N(10);
  var b = N(20);
  var c = a + b;

  print('${c.runtimeType} $c'); // Output: int 30
  print('${a - b} ${a * b} ${a / b}'); // Output: -10 200 0.5
  print('${a.isPositive} ${b.isNegative}'); // Output: true false

  print(a + 1); // Output: 11
  print(2 + a); // Output: 12
}

此例中扩展类型 N 是 类型 int 的透明包装,同时:

  • 添加了新的 getter(isPositive);
  • 重新定义了 + 操作符。

注:meta package 中的注解@redeclare用于告诉编译器你有意地使用与超类相同的成员名称,如果事实并非如此,将收到一个警告。

实现表示类型的超类

// ex568.dart
extension type N(int i) implements num {} // 1

void main() {
  var a = N(10);
  var b = N(20);
  var c = a + b; // 2
  print('${c.runtimeType} $c'); // Output: int 30
  print(1 & 2); // 3 Output: 0

  print(a & b); // 3a
  // Error:
  // The operator '&' isn't defined for the type 'N'.
  // Try defining the operator '&'.
}
  1. 扩展类型 N 实现了类型 num
  2. num 定义了 + 操作符,因此 N 也自然地拥有了 + 操作符;
  3. int 定义了 & 操作符,因此 1 & 2 这样的表达式是有效的;
    numN 都没有定义 & 操作符,因而 a & b 将引起编译错误。

实现基于同一表示类型的其它扩展类型

// ex569.dart
extension type A(num n) {
  num get square => n * n; // 1
}

extension type B(num n) {
  num get cube => n * n * n; // 2
}

extension type C(num n) implements A, B {} // 3

void main() {
  var n = C(3);
  print(n.runtimeType); // Output: int
  print(n.square); // Output: 9
  print(n.cube); // Output: 27
}
  1. num的扩展类型 A定义了一个 名为 square 的 getter;
  2. num的扩展类型 B定义了一个 名为 cube 的 getter;
  3. num的扩展类型 C同时实现了AB,拥有了来自AsquareBcube,这类似于多重继承。

参考资料