Reputation: 6768
To test my understanding of Dart, I made a 2D immutable vector that stores not only its x and y components, but also its angle and length. They are computed only at construction out of the x and y values. I soon realized that final fields need to be set with initializer lists or the this
-parameter shortcut, which don't allow for much computed values. Like this answer points out, I had to use a factory constructor to create my vector out of its x and y components. The factory constructor then validates input and computes the length and angle before calling the private constructor.
import 'dart:math';
class ImmutableVector {
// private fields
final double _x;
final double _y;
final double _angle;
final double _length;
// getters
double get x => _x;
double get y => _y;
double get angle => _angle;
double get length => _length;
/// Constructs a vector out of cartesian components.
///
/// Converts both arguments to doubles.
/// Null values will be treated as zeroes.
factory ImmutableVector.xy(num x, num y) {
x ??= 0.0;
y ??= 0.0;
x = x.toDouble();
y = y.toDouble();
var angle = atan2(y, x) % (2*PI);
var length = sqrt(pow(x, 2) + pow(y, 2));
return new ImmutableVector._raw(x, y, angle, length);
}
/// Constructs a vector by setting the fields directly.
const ImmutableVector._raw(this._x, this._y, this._angle, this._length);
}
But it came to my attention that I could not make the factory constructor const, because const factories can only be redirecting constructors. Is there absolutely no way to make my vector have code in its constructor and still have it an immutable with const constructors? And if so, why?
I'll also mention that I used to throw errors if the values passed were null, but I made it default to zero so I could actually use initializer lists. I then tried to do this, and turns out it works when the constructor isn't a factory:
ImmutableVector.xy(num x, num y) : this._raw(
x = (x ?? 0.0).toDouble(),
y = (y ?? 0.0).toDouble(),
atan2(y, x) % (2*PI),
sqrt(pow(x, 2) + pow(y, 2)));
But as soon as I try to make it const, it tells me that the code in the initializer list contains non-compile-time constants.
The only immutable vector I could find in dart was here on GitHub, and it doesn't do any kind of null-validation or computation on constructor parameters, relying entirely on the fact that methods will break at some point on null-vectors. It also has a constructor that creates a unit vector out of another one with poor performance and repetition thanks to the obligatory initializer list:
const Vector.unit(final num x, final num y, final num z) :
this(x / PMath.len(x, y, z), y / PMath.len(x, y, z), z / PMath.len(x, y, z));
So what should be my conclusion? Did I miss a feature that makes this possible, or should I just give up on using const for this class?
Upvotes: 15
Views: 9463
Reputation: 657238
Dart doesn't execute Dart code during compilation. This is the reason const
constructors can't have a body and why there is no other way to work around this limitation.
If you want to execute code when an instance is created, just don't make use of const
. Const isn't that important in Dart anyway. There were even discussions to remove it from the language because the benefits are not big enough.
Upvotes: 6