Dart 中的构造函数和初始化列表有什么区别?

问题描述 投票:0回答:3

问题

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

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 = Point(3, 4);
  print(p.distanceFromOrigin);
}

我的代码

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

两个输出相同 5.

问题

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

开发环境

致以诚挚的问候,

dart
3个回答
22
投票

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

如果您不想公开 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);
    }

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


17
投票

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

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

将构造函数参数分配给实例的模式 变量是如此常见,Dart 有语法糖来使它变得简单:

class Point {
  double x = 0;
  double y = 0;

  // Syntactic sugar for setting x and y
  // before the constructor body runs.
  Point(this.x, this.y);
}


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

ClassName[.<constructor-name>](<constructor-args>) 
    : <initializer-list> {
  <constructor-body>
}

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


最终或不可为空的成员

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

  • 在会员申报网站,或
  • 直接通过构造函数的参数(使用
    this.
    语法糖),或者
  • 如果还涉及一些计算,则必须是初始化器列表(或者当初始化逻辑无法在初始化器列表中处理时使用工厂构造函数)
class Demo {
  int x; // non-nullable member
  final int? y; // final member

  // ERROR (observe <constructor-body>'s limitation)
  Demo.constructor1(int x, int y) {
    this.x = x; 
    this.y = y;
  }

  // Allowed
  Demo.constructor2(int x, int y)
      : x = x,
        y = y;

  // Better (observe <constructor-args>'s syntactic sugar)
  Demo.constructor3(this.x, this.y);

  // ERROR
  Demo.constructor4(this.x, this.y = this.x + 1);

  // Allowed (observe <initializer-list>'s rescue)
  Demo.constructor5(this.x) : y = x + 1;
}

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


执行顺序

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

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

  1. 初始化列表
  2. 超类的无参数构造函数
  3. 主类的无参数构造函数

如果超类没有未命名、无参数的构造函数, 那么您必须手动调用超类中的构造函数之一。 在冒号 (:) 之后、在 构造函数主体(如果有)。

Jamesdlin 的评论 包含一个很好的总结。



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

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


0
投票

在 Dart 中,

constructors
initializer lists
都用于初始化类的实例变量,但它们的用途不同,并且具有不同的语法结构。

构造函数

构造函数是创建对象时调用的特殊方法。它可以有参数,并且可以包含设置对象状态的逻辑。

class Person {
String name;
int age;

// Constructor
Person(this.name, this.age);
}

void main() {
var person = Person('Alice', 30);
print('${person.name}, ${person.age}');
}

初始化器列表

初始化列表是一种在构造函数体运行之前初始化实例变量的机制。它对于初始化最终字段或更复杂的初始化逻辑特别有用。

class Person {
final String name;
 final int age;

 // Constructor with initializer list
 Person(String name, int age) 
 : this.name = name.toUpperCase(), // Using initializer list
 this.age = age;

 @override
 String toString() => '$name, $age';
 }

 void main() {
 var person = Person('Alice', 30);
 print(person);
 }

主要差异 执行顺序:

初始化列表在构造函数主体之前运行。这意味着您可以在执行构造函数中的任何代码之前初始化字段。

最终字段的使用:

初始化列表对于初始化最终字段特别有用,这些字段只能分配一次。您不能将值分配给构造函数主体中的最终字段。

语法:

在构造函数中,您可以使用初始值设定项列表语法(:fieldName = value)直接设置字段,而在构造函数主体中,您通常使用 this.fieldName。

© www.soinside.com 2019 - 2024. All rights reserved.