2
class A {
  A(int value);
}

class B extends A{
  final int foo;

  B.one(this.foo) : super(foo); // Works

  B.two(int foo) : foo = foo, super(this.foo); // Doesn't work
}

In B.one, I can easily pass the value of foo to super but in B.two, I can't do that. In both cases, the field foo is assigned before calling super, in one case it works and in another it fails. So, the question is at what point the fields are created in the constructor.

Christopher Moore
  • 15,626
  • 10
  • 42
  • 52
iDecode
  • 22,623
  • 19
  • 99
  • 186

2 Answers2

7

Dart constructs objects in two phases: first outside-in and then inside-out.

Initializer lists are executed outside-in (from derived class to base class). After this is done, the object's members should be initialized, the object is considered to be "constructed", and this exists. (That's why you can't use this in a meaningful way in an initializer list; it doesn't exist yet.) (Technically direct member initialization occurs before initializer lists, but I'm lumping them together for simplicity.)

Constructor bodies are then executed inside-out (from base class to derived class).

This approach guarantees that all of the object's members are initialized when the base class constructor body executes, allowing virtual dispatch to occur in the constructor body. (In contrast, C++ constructs objects purely inside-out and disallows virtual dispatch in constructors and destructors. Or contrast to Java where, IIRC, the class mostly defines its own construction order, and the classes are responsible to ensure that any virtual function calls performed by the constructor are safe.)

Without this approach, a Dart constructor body either cannot guarantee safety when executing virtual functions (the Java approach) or must disallow virtual dispatch (the C++ approach).

One consequence of this approach is that final variables can be initialized by initializer lists but not by constructor bodies: when the constructor body executes, all member variables (that aren't late and that aren't nullable) are expected to be initialized already.

Also see:

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

The problem is you can't pass this or its properties to super.
this is only defined in the context of an instance method or a generative constructor.

See: https://dart.dev/tools/diagnostic-messages#invalid_reference_to_this.

This works

class B extends A{
  final int foo;

  B.one(this.foo) : super(foo);

  B.two(int foo) : foo = foo, super(foo); 
}

The difference In B.one, you are passing to super foo as the property foo but in B.two you are passing foo from the parameter on two

Take a look at this;

class A {
  A(int value) {
    print(value);
  }
}

class B extends A {
  final int foo;

  B.one(this.foo) : super(foo);

  B.two(int foo): foo = 5, super(foo);
}

main() {
 B b = B.two(100); //Prints 100;
 print(b.foo); //Prints 5
}
Josteve
  • 11,459
  • 1
  • 23
  • 35
  • Referring your first line 'you can't pass this or ...', do you think `foo` passed to `super` in `B.one` constructor is actually a class property? – iDecode Aug 08 '20 at 13:36
  • It's a property of the object created. – Josteve Aug 08 '20 at 13:52
  • So, in `B.one`, I am passing `foo` property (created) to `super` and in `B.two`, I am also passing the same `foo` property (created) to `super`. What's the difference? – iDecode Aug 08 '20 at 15:34
  • 1
    In `B.one` you are passing the value of the *variable* `foo` introduced by the (initializing formal) parameter `this.foo` to the super-constructor, not `this.foo`. The `this.foo` parameter does two things: It initializes the `foo` field to the value of stores the value of the argument, and it introduces a final variable named `foo` bound to the same value in the scope of the initializer list. That's the variable you are referring to. – lrn Aug 08 '20 at 17:02
  • @Locked Not sure how your updated code answers the question. In `B.two` you're simply passing the passed value of `foo` to `super`, not the instance field `foo`'s value, which I want to pass. – iDecode Aug 08 '20 at 17:23