185 lines
4.7 KiB
Plaintext
185 lines
4.7 KiB
Plaintext
A constructor that forwards all its parameters to a https://dart.dev/language/constructors#non-default-superclass-constructors[superclass constructor] should use https://dart.dev/language/constructors#super-parameters[`super` parameters] (e.g. `ConstructorName(super.field)`) instead of manually forwarding each parameter (as in `ConstructorName(int field) : super(field)`).
|
|
|
|
== Why is this an issue?
|
|
|
|
In Dart, constructors can "forward" their parameters to a constructor of the parent class, using the `super` keyword.
|
|
|
|
[source,dart]
|
|
----
|
|
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 https://dart.dev/language/constructors#super-parameters[`super` parameters] instead of manually forwarding each parameter.
|
|
|
|
[source,dart]
|
|
----
|
|
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:
|
|
|
|
[source,dart]
|
|
----
|
|
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:
|
|
|
|
[source,dart]
|
|
----
|
|
class Child extends Parent {
|
|
const Child(int field1, int field2) : super(field2, field1); // Non applicable
|
|
}
|
|
----
|
|
|
|
or their values transformed:
|
|
|
|
[source,dart]
|
|
----
|
|
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:
|
|
|
|
[source,dart]
|
|
----
|
|
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:
|
|
|
|
[source,dart]
|
|
----
|
|
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:
|
|
|
|
[source,dart]
|
|
----
|
|
class Child extends Parent {
|
|
Child(int field1) : super(field1, field1.hashCode); // Non applicable
|
|
}
|
|
----
|
|
|
|
That includes swapping them:
|
|
|
|
[source,dart]
|
|
----
|
|
class Child extends Parent {
|
|
Child(int field1, int field2) : super(field2, field1); // Non applicable
|
|
}
|
|
----
|
|
|
|
== How to fix it
|
|
|
|
Remove the call to the superclass constructor and replace normal parameters with `super` parameters, matching the names of the parameter in the parent class.
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,dart,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
class Point {
|
|
final int x;
|
|
final int y;
|
|
|
|
const Point(this.x, this.y);
|
|
}
|
|
|
|
class ThreeDPoint extends Point {
|
|
final int z;
|
|
|
|
const ThreeDPoint(int x, int y, this.z): super(x, y);
|
|
}
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,dart,diff-id=1,diff-type=compliant]
|
|
----
|
|
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);
|
|
}
|
|
----
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* Dart Docs - https://dart.dev/tools/linter-rules/use_super_parameters[Dart Linter rule - use_super_parameters]
|
|
* Dart Docs - https://dart.dev/language/constructors#non-default-superclass-constructors[Language - Non-default superclass constructors]
|
|
* Dart Docs - https://dart.dev/language/constructors#super-parameters[Language - Super parameters]
|
|
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== Message
|
|
|
|
* Parameters '<parameterName>' could be super parameter.
|
|
* Parameters '<parameterName1>' and '<parameterName2>' could be super parameters.
|
|
* Parameters '<parameterName1>' (, '<parameterNameI>')+, and '<parameterNameN>' could be super parameters.
|
|
|
|
=== Highlighting
|
|
|
|
The identifier of the constructor at the declaration site.
|
|
|
|
endif::env-github,rspecator-view[]
|