How do I initialize a final class property in a constructor?

58,886

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);
  }
}
Share:
58,886
Blackbam
Author by

Blackbam

Hello! My name is David. I have studied Software Engineering &amp; 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, 2021

Comments

  • Blackbam
    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
    Kamafeather almost 6 years
    I'd add to familiarise with final and its implication within constructors, regarding separation of concerns. What's final 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 it final (or, in case you need more operations/manipulation for setting a final, you can see the factory suggestion by @rkj )
  • Nk54
    Nk54 about 5 years
    Hello, 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
    Nk54 about 5 years
    I 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
    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
    Bansook Nam almost 5 years
    and add , super(key: key) in the end of it.
  • Syph
    Syph almost 4 years
    Late 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
    rkj almost 4 years
    The 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
    kkessell almost 3 years
    The factory keyword tells you that you need to construct your return value yourself (contrary to without using it it will be created for you).