首页 > 解决方案 > Dart:为什么继承的字段不能用作构造函数参数?

问题描述

以下代码将不会运行:

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 中使用这些字段,似乎只有构造函数在接受这些作为参数时存在问题。但为什么?

标签: dartinheritancemixins

解决方案


Dart 实例变量(又名“字段”)引入了一个存储单元、一个隐式 getter 和一个隐式 setter(除非变量 isfinal和 not late,则没有 setter)。

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

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

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

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


推荐阅读