【问题标题】:Dart: Why are inherited fields not usable as constructor arguments?Dart:为什么继承的字段不能用作构造函数参数?
【发布时间】:2021-11-03 23:16:44
【问题描述】:

以下代码不会运行:

class X{
 double? x;
}

mixin Y{
 double? y;
}

class Z extends X with Y {
 double? z; 
 Z(this.x, this.y, this.z)
}

编译器会抱怨 this.x 和 this.y 不是封闭类中的字段:

lib/physicalObject.dart:20:10: Error: 'x' isn't an instance field of this class.
Z(this.x, this.y, this.z)
     ^

lib/physicalObject.dart:20:18: Error: 'y' isn't an instance field of this class.
Z(this.x, this.y, this.z)
             ^

当然不是这样,字段x和y是继承自父类和mixin的。我可以在子类 Z 中使用这些字段,似乎只有构造函数在接受这些作为参数时存在问题。但为什么呢?

【问题讨论】:

  • 如果X 的默认构造函数也初始化了x,你期望会发生什么?如果xfinal 会怎样?
  • 很好的问题,我不确定,但是当我定义以下构造函数时会出现同样的问题: Z({ double?x, double?y, double?z}) { this.x = x;这个.y = y;这个.z = z; } 然而,dart 让我这样做
  • 您的最后一个示例定义明确;当Z的构造函数体执行时,基类已经被初始化。 (如果 X.xfinal,您的示例将失败。)请参阅 stackoverflow.com/a/63319094
  • 你是说如果我写Z(this.z),z是在基类初始化之前设置的吗?我假设 Z(this.z) 和 Z(double?z) {this.z = z} 基本上是同一事物的不同符号。
  • 是的,这就是我要说的。 Z(this.z)Z(double? z) { this.z = z; } 不一样,因为构造函数体不能用于初始化(非latefinal 字段。

标签: dart inheritance mixins


【解决方案1】:

Dart 实例变量(又名“字段”)引入了一个存储单元、一个隐式 getter 和一个隐式 setter(除非变量是 final 而不是 late,那么就没有 setter)。

构造函数初始化器可以直接初始化存储单元,不需要调用setter,即使没有setter。这就是Z(this.z)Z(double? z) : this.z = z; 所做的,它将一个值直接存储到实例变量的存储中。

Z(double? z) { this.z = z; } 这样的构造函数不会直接存储到单元格中,而是调用名为z= 的setter,然后它可能会存储到(必须是非最终的或后期的)变量的存储中。 或者它可能不会。 Setter 是虚拟的,子类可以覆盖它们,{ this.z = z; } 赋值会调用被覆盖的 setter。

一般来说,子类看不到超类的变量,它看到的只是超类的接口,它只暴露了getter和setter。如果超类决定将字段声明更改为 getter 和 setter 声明对,那么它们可以。它是非破坏性的,子类无法区分。 因此,如果子类能够以某种方式看到一个变量可以初始化(不仅仅是分配给),那么我们就破坏了这种对称性,并且超类被锁定为拥有一个可初始化的变量。

初始化必须在声明实例变量的类中进行因为如果没有,我们会通过泄漏 getter/setter 是否由字段支持来破坏该类接口的抽象,然后我们将班级锁定在该选择中。这与为什么 Dart 拥有它所拥有的那种 getter/setter 声明完全相反,即无论您使用其中一个还是另一个,都使其成为实现选择,而不是公共 API 选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 2020-02-28
    • 2013-08-27
    • 1970-01-01
    • 1970-01-01
    • 2019-10-31
    • 2012-01-29
    相关资源
    最近更新 更多