命名构造函数是 Dart 中生成构造函数的子集吗?

Sur*_*gch 5 constructor dart

在构造器的Dart 语言之旅中,它给出了一个生成式构造器的示例:

class Point {
  double x, y;

  Point(double x, double y) {
    // There's a better way to do this, stay tuned.
    this.x = x;
    this.y = y;
  }
}
Run Code Online (Sandbox Code Playgroud)

后来它给出了一个命名构造函数的例子:

class Point {
  double x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}
Run Code Online (Sandbox Code Playgroud)

这让我相信,当构造函数使用与类相同的名称时,它是一个生成构造函数:

Point(this.x, this.y);
Run Code Online (Sandbox Code Playgroud)

但是当有一个额外的标识符时,它就是一个命名构造函数:

Point.origin() {
  x = 0;
  y = 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,在我的另一个答案中,Dart 专家将我的“命名构造函数”更改为“生成构造函数”。这让我意识到我可能误解了它们之间的区别。命名构造函数是生成构造函数的子集吗?如果是这样,我如何调用只有类名而没有附加附加标识符的构造函数?

术语“命名构造函数”似乎不在语言规范中

lrn*_*lrn 12

在 Dart 中,任何构造函数要么是生成构造函数,要么是工厂构造函数。如果factory前面有写,就是工厂构造函数,否则就是生成构造函数。

生成构造函数总是创建它所属的精确类的实例。工厂构造函数(几乎)只是一个静态函数,其返回类型是它所属类的类型。它可以返回它的任何子类型,但它本身不会创建任何新对象。

构造函数可以是namedunnamed。对于 class Foo,命名的构造函数Foo是“未命名”(或者,实际上,“空命名”)构造函数,并且Foo.someName是命名构造函数。构造函数是命名的还是未命名的与它是生成式还是工厂无关。Dart 没有重载(在同一范围内有多个具有相同名称的声明,通常由参数类型区分),因此如果没有命名构造函数,每个类只能有一个构造函数。命名构造函数允许一个类拥有任意数量的构造函数,并且每个构造函数都可以是该语言允许的构造函数的任何变体。

构造函数可以是forwarding,也可以是non-forwarding,因为没有更好的名称。转发生成构造函数必须转发到同一类的生成构造函数。例子:

class Point {
  final double x, y;
  const Point(this.x, this.y);  // Generative, unnamed, non-forwarding, const.
  const Point.diagonal(double xy) : this(xy, xy); // Generative, named, forwarding, const.
}
Run Code Online (Sandbox Code Playgroud)

转发工厂构造函数将其参数转发给不同的构造函数。例子:

abstract class Point {
  double get x;
  double get y;
  const factory Point(this.x, this.y) = _Point;  // Factory, unnamed, forwarding, const.
}
class _Point implements Point {
  final double x, y;
  const _Point(this.x, this.y); // Generative, unnamed, non-forwarding, const.
}
Run Code Online (Sandbox Code Playgroud)

这两种转发构造函数,generative 和 factory,并没有真正的关联。它们以完全不同的方式工作,但都将返回对象的工作委托给另一个构造函数,并且它们不能有一个主体。同样,命名与所有这些无关。

最后,构造函数可以是也可以const不是。const 生成构造函数要么转发到另一个 const 生成构造函数,并且: this(...)参数必须是潜在的常量表达式,要么是非转发的,然后所有初始化表达式必须是潜在的常量表达式并且不能有主体。const 工厂构造函数必须转发到另一个 const 构造函数(无论是工厂还是生成)。const 构造函数不能有主体,非转发工厂构造函数必须有主体。

在所有这些不同的构造函数中,只有非转发生成构造函数才能真正创建一个新对象本身。这就是真正的行动发生的地方。这是最基本的构造函数,它看起来像:

  Foo.bar(this.value, int otherValue) : _otherValue = otherValue, super.bar(42) {
    this.doSomething();
  }
Run Code Online (Sandbox Code Playgroud)

非重定向工厂构造函数是唯一可以使用初始化形式(形式的参数this.value)的地方。初始化列表可以初始化在同一个类中声明的实例变量,但如果没有,显然可以为空。最后的超调用必须调用超类的生成构造函数,但super()如果省略则默认为。正文是新对象可用的第一个位置(作为this),但如果正文为;空,则可以省略(由 替换)。这就是为什么最简单的构造函数只是Foo();(如果您没有声明其他构造函数,这也是您获得的默认构造函数)。