Dart子类两种构造函数差异解析:为何一个可行一个报错?
问题背景
下面是继承自类A的类B代码:
class A { A(int value){ print(value); } } class B extends A{ int x; B.one(this.x) : super(x); B.two(int x) : this.x = x, super(this.x); }
类B包含B.one()和B.two()两个构造函数,仅B.one()可正常工作,B.two()调用super(this.x)会报错。原本认为B.one()是B.two()的简写,却疑惑两者传参给父类的方式存在差异。
尝试将B.two()修改为以下代码后可正常运行:
class A { A(int value){ print(value); } } class B extends A{ int x; B.one(this.x) : super(x); B.two(int x) : this.x = x, super(x); }
修改后移除super中的this关键字即可正常运行,但不解为何B.one()可直接传递字段x给父类,而B.two()却需使用构造函数参数x。
原因解析
核心在于Dart构造函数初始化列表的执行顺序:
- 第一步:先确定构造函数所有参数的具体值,这些参数此时处于可用状态。
- 第二步:执行父类构造函数(即
super()调用),这一步会在子类的实例字段初始化之前完成。 - 第三步:才会执行子类初始化列表里的字段赋值操作(比如
this.x = x)。
对两个构造函数分别拆解:
B.one(this.x) : super(x)
这里的this.x是Dart语法糖,本质是构造函数接收参数x,并直接将该参数赋值给实例字段x。而super(x)中的x指代的是构造函数的参数x——这个参数在第一步就已确定值,因此可以直接传递给父类构造,不会有问题。原始错误的
B.two(int x) : this.x = x, super(this.x)super(this.x)里的this.x是子类的实例字段,但按照执行顺序,父类构造会先执行,此时this.x = x还未执行,实例字段x仍为默认的null,将null传给需要int类型的父类构造自然会触发错误。修改后的
B.two(int x) : this.x = x, super(x)super(x)使用的是构造函数的参数x,该参数在第一步已确定值,无论后续this.x = x何时执行,都能正常将参数值传递给父类构造,因此可以正常运行。
简单来说:B.one()里的super(x)用的是构造函数参数,而非已初始化的实例字段;B.two()若写super(this.x),则是在实例字段初始化前引用它,必然出错。
内容的提问来源于stack exchange,提问作者newlunch

