3

Often times I have an instance field that needs to be initialized in a constructor. For example, it might be needed to be calculated based on other instance fields, hence I can't initialize it inline (where declared) or with a constructor initializer list.

But it either has to be nullable, or declared late, if I need to initialize it in constructor. The rationale behind late keyword is that programmer states "I'll initialize this before using, trust me", when it can not be determined by the compiler that initialization will take place before first usage. BUT: this "programmer guarantee" seems A) terrible and B) unnecessary in case of constructors, because it can be determined by compiler whether the field was initialized in a constructor (and constructor itself is obviously guaranteed to execute before any other instance methods).

Obvious downside to using late fields in such scenarios is that nothing enforces them compile-time to be actually initialized during construction (or anywhere, for that matter). Plus, every time the late field is read, a runtime check is inserted to make sure it has been assigned a value - I don't need that when I initialize in constructors.

Therefore, it seems that, technically it should be possible to have non-nullable non-late fields that are initialized within a constructor body (and if they are not - compiler can throw an error).

So what is the rationale of requiring constructor-initialized fields to be either nullable, or declared as late? Is there a technical reason why this limitation is imposed, or is it just a design oversight by the Dart team?

Titan
  • 2,875
  • 5
  • 23
  • 34
  • Why cant you initialize it in an initializer list? Can you provide some simple example? – croxx5f Aug 09 '21 at 19:47
  • The constructor *body* can execute arbitrary code (including instance methods) in any order. Therefore the object *must* be guaranteed to be initialized. See https://stackoverflow.com/q/63313116/. – jamesdlin Aug 09 '21 at 19:51
  • @jamesdlin I don't see how this question is a duplicate of a linked one, or how it (completely) answers my question. Could you please elaborate (in a dedicated answer) why exactly is it impossible to deterministically initialize fields within a constructor body. And/or re-open my question. – Titan Aug 09 '21 at 20:08
  • I know this doesn't answer your question, but consider using a `factory` constructor method if you really really need to implement a constructor body. – venir Aug 09 '21 at 22:12

2 Answers2

1

Dart executes constructor bodies inside-out, from base class to derived class. This allows virtual dispatch to occur in the constructor body. The fact that virtual dispatch can occur in the constructor body means that the compiler cannot statically determine what code will be executed by the constructor body, and therefore it cannot deduce what the constructor body might ultimately initialize.

That the constructor body can execute arbitrary code (including callbacks) that might initialize members or that might depend on initialized members makes it even more complicated.

Furthermore, allowing members to be not initialized when the constructor body runs would be error-prone and a source for confusion. For example, with:

class SomeClass {
  int member;

  SomeClass() {
    updateMember(0);
  }

  void updateMember(int value) {
    print(value); // Oops.
    member = value;
  }
}

With Dart's current design, all instance methods (and their overrides) can be guaranteed that members are initialized when the method is called. If members were allowed to be uninitialized when the constructor body is executed, that would not longer be true, and all instance methods then would need to consider if they might be invoked from the constructor (or from a base class constructor), possibly indirectly from other method calls, and whether accessed members might not be initialized yet.

(I'll grant that that previous point isn't terribly strong since it currently can still happen that a member is initialized to an object that the constructor body must mutate, but typically instance methods receiving an empty List, Map, etc. is less of a problem than receiving uninitialized members. The above situation also could happen with late members, but that's the baggage that comes with choosing to use late.)

Null-safety disallows the possibility of accessing uninitialized non-late variables, but your proposal would make that possible.

Also, because there is a distinction between initializing members via an initializer list and via a constructor body, people are encouraged to use initializer lists when possible.

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
0

The point that you are ignoring here is that when you want to pass a variable to a constructor you will definitely initialize it, otherwise you wouldn't be able to use that widget because you have to pass the variables needed to its constructor. so this late or nullable keywords can be used for the values that you are trying to pass to a widget and not in the widget itself that you are passing them to, but before it.

Benyamin
  • 1,008
  • 11
  • 18