Dart 中的构造函数和初始值设定项列表有什么区别?

Ryo*_*eOK 9 dart

问题

下面两个代码的输出是一样的,但本质区别是什么?

Dart 语言之旅 - 初始值设定项列表

import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

main() {
  var p = new Point(3, 4);
  print(p.distanceFromOrigin);
}
Run Code Online (Sandbox Code Playgroud)

我的代码

  Point(this.x, this.y)
      : distanceFromOrigin = sqrt(x * x + y * y);
Run Code Online (Sandbox Code Playgroud)

两个输出相同 5。

  • 我应该如何正确使用构造函数和初始化列表?

开发环境

此致,

Ard*_*der 15

无论是乘坐火车还是婴儿车,一旦到达车站,只要到达目的地就没有什么“本质”的区别了。

\n

使用“构造函数”和“初始化器列表”,您确实获得了相同的输出,但该示例并不是 Dart 中这些功能支持的唯一用例。此外,较短的形式(使用“构造函数”作为“初始化列表”的替代)只是Dart 将其作为“语法糖”提供的常见表现形式常见表现形式:

\n
\n

将构造函数参数分配给实例变量的模式非常常见,Dart 提供了语法糖来使其变得简单:

\n
\n
class Point {\n  double x = 0;\n  double y = 0;\n\n  // Syntactic sugar for setting x and y\n  // before the constructor body runs.\n  Point(this.x, this.y);\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n
\n

我已经根据您的问题/示例使用了术语“构造函数”和“初始化列表”,但为了清楚地提及差异,我将使用以下术语来获得更广泛的视角:

\n
ClassName[.<constructor-name>](<constructor-args>) \n    : <initializer-list> {\n  <constructor-body>\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在答案的这一部分之前,对“构造函数”的关注在很大程度上忽略了构造函数的主体,而这种情况恰好是这样,因为该示例可能旨在引入初始值设定项列表,这从最终成员的参与中可以明显看出。

\n
\n

最终或不可为 null 的成员

\n

构造函数的主体不能用于初始化不可为空和/或最终成员。可以做到

\n
    \n
  • 在会员声明网站,或
  • \n
  • 直接通过构造函数的参数(使用this.语法糖),或者
  • \n
  • 必然是初始化列表(或者当初始化逻辑无法在初始值设定项列表中处理时使用工厂构造函数)
  • \n
\n
ClassName[.<constructor-name>](<constructor-args>) \n    : <initializer-list> {\n  <constructor-body>\n}\n
Run Code Online (Sandbox Code Playgroud)\n

除此之外,这不会使初始化程序列表替代构造函数主体,因为初始化程序列表的功能非常有限

\n
\n

执行顺序

\n

另一个重要的区别是它们的执行顺序,从设计的角度来看,这可能很重要,尤其是涉及继承时。它在语言之旅中得到了恰当的记录记录:

\n
\n

[...]执行顺序如下:

\n
    \n
  1. 初始化列表
  2. \n
  3. superclass\xe2\x80\x99s 无参数构造函数
  4. \n
  5. 主类\xe2\x80\x99s 无参数构造函数
  6. \n
\n

如果超类\xe2\x80\x99t 没有未命名、无参数的构造函数,\n那么您必须手动调用超类中的构造函数之一。\n在冒号 (:) 之后、构造函数之前\n指定超类构造函数身体(如果有的话)。

\n
\n

詹姆斯德林的评论包含了很好的总结。

\n
\n
\n
\n

我应该如何正确使用构造函数和初始化列表?

\n
\n

牢记上述差异,“正确”用法客观上遵守此类语言限制,但在两种用法都合适的情况下,我认为您使用语法糖来编写惯用的 Dart 代码是正确的,除非类似的要求另有要求强制的风格指南。

\n


die*_*per 7

没有区别,结果将是相同的,只是您可以利用不同类型的构造函数。

如果您不想公开 Point 中定义的变量并将它们标记为私有,则初始化程序将是一个不错的选择。

    class Point {
      final num _x;
      final num _y;
      final num _distanceFromOrigin;

      Point(x, y)
          : _x = x,
            _y = y,
            _distanceFromOrigin = sqrt(x * x + y * y);
    }
Run Code Online (Sandbox Code Playgroud)

还可以查看带有可选参数或工厂构造函数的构造函数。

  • 明白了。根据 Dart 语言之旅,“除了调用超类构造函数之外,您还可以在构造函数主体运行之前初始化实例变量。” 所以,如果没有Body,就代表我可以按照自己的喜好来使用。 (2认同)