In Dart, constructors can "forward" their parameters to a constructor of the parent class, using the super
keyword.
class ComplexNumber {
final double real;
final double imaginary;
const ComplexNumber(this.real, this.imaginary);
}
class RealNumber extends ComplexNumber {
const RealNumber(double real) : super(real, 0); // Forwards the `real` parameter
}
When a constructor forwards all its parameters to a superclass constructor, and does not alter them in any way, it is recommended to use super
parameters instead of manually forwarding each parameter.
class Point {
final int x;
final int y;
const Point(this.x, this.y);
}
class ThreeDPoint extends Point {
final int z;
const ThreeDPoint(super.x, super.y, this.z);
}
What is the potential impact?
Manually forwarding parameters is more verbose. It can also be error-prone. For instance, if the superclass constructor has multiple parameters of
the same type, there is the risk of mixing them up:
class Parent {
final int field1;
final int field2;
const Parent(this.field1, this.field2);
}
class Child extends Parent {
const Child(int field2, int field1) : super(field2, field1); // Wrong order
}
This cannot happen with super
parameters, no matter what is the order of parameters declared in the Child
class, as they
are matched by name.
Exceptions
The rule doesn’t apply if parameters are altered before being passed to the superclass constructor.
For example, if they need to be swapped:
class Child extends Parent {
const Child(int field1, int field2) : super(field2, field1); // Non applicable
}
or their values transformed:
class Child extends Parent {
const Child(int field1, int field2) : super(field1 + field2, field1 - field2); // Non applicable
}
On the other hand, changing the type of the parameter in the subclass with a compatible one is not considered a transformation, so the rule
applies:
class Child extends Parent {
const Child(dynamic field1, dynamic field2) : super(field1, field2); // Non compliant
}
Similarly, the rule applies if the parameter has a different name in the child class:
class Child extends Parent {
const Child(int field3, int field4) : super(field3, field4); // Non compliant
}
However, it’s enough for a single parameter to be altered for the rule to be considered as not applicable:
class Child extends Parent {
Child(int field1) : super(field1, field1.hashCode); // Non applicable
}
That includes swapping them:
class Child extends Parent {
Child(int field1, int field2) : super(field2, field1); // Non applicable
}