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

时间:2020-08-08 08:02:31

标签: dart constructor

Dart language tour中的构造函数中,给出了一个生成构造函数的示例:

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

稍后提供了named constructor的示例:

class Point {
  double x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}

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

Point(this.x, this.y);

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

Point.origin() {
  x = 0;
  y = 0;
}

但是,在my answers的另一位专家中,Dart专家将我的“命名构造函数”的措词改为了“生成构造函数”。这使我意识到我可能误解了两者之间的区别。命名构造函数是生成构造函数的子集吗?如果是这样,我该如何调用仅具有类名称而没有附加标识符的构造函数?

“命名构造函数”一词似乎不在language spec中。

1 个答案:

答案 0 :(得分:10)

在Dart中,任何构造函数都是 generative 构造函数或 factory 构造函数。 如果在前面说factory,则是工厂构造函数,否则是生成构造函数。

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

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

构造函数可以是 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.
}

转发工厂构造函数将其参数转发给其他构造函数。示例:

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

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

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

在所有这些不同的构造函数中,只有非转发的生成构造函数实际上可以自己创建一个新对象。这就是真正的动作发生的地方。那是最基本的构造函数,看起来可能像这样:

  Foo.bar(this.value, int otherValue) : _otherValue = otherValue, super.bar(42) {
    this.doSomething();
  }

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