How do I initialize a final class property in a constructor?
Solution 1
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));
}
Solution 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);
}
}
Solution 3
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;
Solution 4
I've had a similar problem: I was trying to initialise a final
field from the constructor, while simultaneously calling a super constructor. You could think of the following example
class Point2d {
final int x;
final int y;
Point2d.fromCoordinates(Coordinates coordinates)
: this.x = coordinates.x,
this.y = coordinates.y;
}
class Point3d extends Point2d {
final int z;
Point3d.fromCoordinates(Coordinates coordinates)
:this.z = coordinates.z,
super.fromCoordinates(coordinates);
}
/// Demo class, to simulate constructing an object
/// from another object.
class Coordinates {
final int x;
final int y;
final int z;
}
Well apparently this works. You can initialise your final fields by using the above syntax (check Point3d's constructor) and it works just fine!
Run a small program like this to check:
void main() {
var coordinates = Coordinates(1, 2, 3);
var point3d = Point3d.fromCoordinates(coordinates);
print("x: ${point3d.x}, y: ${point3d.y}, z: ${point3d.z}");
}
It should prin x: 1, y: 2, z: 3
Solution 5
I've come to a dilemma here where I wanted to initialize a final List with no items, and a Stream
to be defined inside the constructor (like in this case distanceFromOrigin
).
I couldn't do that with any of the answers below, but I mixed them both and it worked.
Example:
class MyBloc {
final BehaviorSubject<List<String>> itemsStream;
final List<String> items = [];
MyBloc() : this.itemsStream = BehaviorSubject<List<String>>.seeded([]) {
items.addAll(List.generate(20, (index) => "Hola! I'm number $index"));
itemsStream.add(items);
}
}
Blackbam
Hello! My name is David. I have studied Software Engineering & Internet Computing at the Vienna University of Technology. I am a passionate lead developer, enterprise software engineer and senior web developer. Are you looking for a smart, creative and open-minded IT professional who loves new challenges? Hire me! Especially for complex high-quality Symfony, Laravel, Pimcore or WordPress projects. Be free to contact me via my website or Stack Overflow Careers. Author of a useful general purpose PHP toolkit.
Updated on May 06, 2021Comments
-
Blackbam about 3 years
In Java you are allowed to do this:
class A { private final int x; public A() { x = 5; } }
In Dart, I tried:
class A { final int x; A() { this.x = 5; } }
I get two compilation errors:
The final variable 'x' must be initialized.
and
'x' can't be used as a setter because its final.
Is there a way to set final properties in the constructor in Dart?
-
Kamafeather almost 6 yearsI'd add to familiarise with
final
and its implication within constructors, regarding separation of concerns. What'sfinal
is meant to be assigned just once; so Drat provides its interface/sugar to quicker assign value to those properties (common pattern). While, if you are gonna change ´x´ property's value, then don't make itfinal
(or, in case you need more operations/manipulation for setting a final, you can see thefactory
suggestion by @rkj ) -
Nk54 about 5 yearsHello, what if the class Point inherits another class and must call to its parent constructor with super(x,y) AND instantiating a final field like distanceFromOrigin?
-
Nk54 about 5 yearsI found the answer to my question : just separate both instruction with a coma. Notice that the super() call must be the last thing.
-
Bansook Nam almost 5 years@OliverDixon I found this is working
dart class Point { final num x; final num y; final num distanceFromOrigin; final num distanceFromOrigin2; Point(this.x, this.y) : distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2)), distanceFromOrigin2 = sqrt(pow(x, 2) + pow(y, 2)); }
-
Bansook Nam almost 5 yearsand add
, super(key: key)
in the end of it. -
Syph almost 4 yearsLate question but what does the factory keyword do here. I thought it was used for deciding whether to return an existing or new object, but you always return a new one here right
-
rkj almost 4 yearsThe factory is needed, as the Point object cannot be created right away - we first need to compute
distance
, only then we have everything ready to initialize all the final variables. -
kkessell almost 3 yearsThe
factory
keyword tells you that you need to construct your return value yourself (contrary to without using it it will be created for you).