How do I initialize a final class property in a constructor?
You cannot instantiate final fields in the constructor body. There is a special syntax for that:
class Point {
final num x;
final num y;
final num distanceFromOrigin;
// Old syntax
// Point(x, y) :
// x = x,
// y = y,
// distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
// New syntax
Point(this.x, this.y) :
distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
}
You can make it even shorter with this.
syntax in the constructor (described in https://www.dartlang.org/guides/language/language-tour#constructors):
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(this.x, this.y)
: distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
}
If you have some more complicated initialization you should use factory constructor, and the code become:
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point._(this.x, this.y, this.distanceFromOrigin);
factory Point(num x, num y) {
num distance = distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
return new Point._(x, y, distance);
}
}
Here is a simplified summary of the ways to initialize a final class variable.
class MyClass {
final int x; // <-- initialize this
}
Initializer value
class MyClass {
final int x = 'hello'.length;
}
You'd only use final
if the initialization could only be done at runtime. Otherwise, static const
is better:
class MyClass {
static const int x = 0;
}
Initializer formal
class MyClass {
MyClass(this.x);
final int x;
}
This is the most common approach.
Initializer list
class MyClass {
MyClass(int x)
: _x = x;
final int _x;
}
This is useful when you want to keep a field private.
Default parameter value
You can surround the parameter with square brackets ([]
) for an unnamed parameter or curly braces ({}
) for a named parameter and then give it a default value.
class MyClass {
MyClass({this.x = 0});
final int x;
}
This is useful if you want to make the parameter optional.
You could accomplish the same thing with an initializer list as well:
class MyClass {
MyClass({int? x})
: _x = x ?? 0;
final int _x;
}
Late initialization
class MyClass {
MyClass(String? a) {
x = a?.length ?? 0;
}
late final int x;
}
This is useful if you need to do more complex initialization than is allowed in the initializer list. For example, I've done this when initializing a gesture recognizer in Flutter.
Lazy initialization
Another advantage of using late
is that it doesn't initialize a value until you access the value.
class MyClass {
late final int x = _doHeavyTask();
int _doHeavyTask() {
var sum = 0;
for (var i = 0; i < 100000000; i++) {
sum += 1;
}
return sum;
}
}
This is useful if you have a heavy calculation that you only want call if you absolutely need it.
This doesn't initialize x
:
final myClass = MyClass();
But this does initialize x
:
final myClass = MyClass();
final value = myClass.x;