Почему конструктор фабрики нельзя использовать при вызове суперкласса?

#dart

Вопрос:

Минимальный воспроизводимый код:

 class Foo {
  Foo();
  factory Foo.named() => Foo();
}

class Bar extends Foo {
  Bar() : super.named(); // Error
} 
 

Поскольку async-await в фабричном конструкторе ничего нет, и он мгновенно вызывает конкретный конструктор, так почему же ему не разрешается использовать фабричный конструктор подобным образом?

До нулевой безопасности, null может быть возвращен из factory конструктора, но это уже не тот случай. Итак, я думаю, что теперь должен быть разрешен конструктор фабрики.

Ответ №1:

заводская функция может возвращать подкласс, что означает, что она может вызывать функцию инициализации подкласса.

если подкласс self может вызывать функцию super.factory, то он становится циклическим.

Ответ №2:

Вызов порождающего конструктора создает новый объект, затем запускает конструктор и конструкторы суперкласса для инициализации этого нового объекта. Они являются инициализаторами, которые инициализируют объект, созданный (теперь часто неявным) new оператором.

Суперконструктор, связанный цепочкой (не перенаправляющим) генеративным конструктором, вызывается для инициализации одного и того же объекта и обеспечения полной инициализации его полей, прежде чем предоставлять кому-либо доступ к этому объекту.

Вызов конструктора фабрики не создает никакого нового объекта, он просто выполняет тело этого конструктора, который может делать все, что может обычный код. (Это может включать или не включать вызов конструктора генерации для создания объекта. Он также может просто бросить и никогда не создавать никаких объектов.) Конструктор фабрики не может получить доступ this , потому что нет «текущего объекта», прежде чем он его вернет.

Давайте предположим, что Foo у int foo = 42; этого есть поле. Конструктор фабрики, подобный Foo.named here, создает новый объект путем вызова Foo() — порождающего конструктора. Когда вы это сделаете new Bar() , вы создадите новый Bar объект и попросите суперконструкторов инициализировать этот объект. Конструктор named фабрики не имеет доступа к этому новому объекту , который был бы создан путем вызова new Bar() , он не может получить доступ this и не может помочь инициализировать foo поле этого объекта. Он создает новый, не связанный Foo объект, и теперь у вас есть 1) частично инициализированный Bar объект и 2) Foo объект, ни один из которых не может быть результатом вызова Bar конструктора.

Ваши генеративные конструкторы должны вызывать генеративный конструктор суперкласса, который делает то же самое до тех пор, пока не достигнет Object конструктора. Это единственный способ убедиться, что вновь созданный объект полностью инициализирован.

Комментарии:

1. «Именованный конструктор не имеет доступа к объекту, который будет создан путем вызова new Bar ()» — я заблудился в этой строке. Не могли бы вы, пожалуйста, объяснить это немного подробнее.

2. Трудно описать гипотетику, которая не имеет смысла. Я добавил еще немного текста. В принципе, конструктор генерации автоматически получает объект «это» для инициализации, используя свой список инициализаторов, а конструктор фабрики этого не получает, поэтому он не может инициализировать создаваемый объект. Таким образом, вы не можете связать шаг инициализации генерации с помощью конструктора фабрики, потому что он не может выполнять то, что должен выполнять конструктор генерации: инициализировать новый объект.

3. Спасибо, теперь все гораздо яснее. Хотя я не понял в этой строке ни одной элементарной вещи ... ask the super-constructors to initialize that object . О чем object мы здесь говорим, Bar ? И если это так Bar , то так ли все работает в Dart, что, когда мы делаем super(...) это из конструктора базового класса, он в основном передает экземпляр базового класса суперклассу для инициализации, т. Е. инициализирует ли суперкласс экземпляр базового класса? Я думаю, что это то, что вы также упомянули в комментарии (поскольку я не из CS, мне немного сложно понять вещи при первом чтении).

4. Когда вы это сделаете new Bar() , новый Bar объект немедленно создается в неинициализированном состоянии. Вызывается конструктор генерации для Bar , который должен инициализировать любые поля , введенные с Bar помощью инициализаторов полей, инициализирующих формалов или назначений списка инициализаторов. Затем вызывается конструктор генерации суперкласса для инициализации того же объекта, только для любых полей, введенных суперклассом, Foo здесь. Затем вызывается его конструктор суперкласса (который Object здесь является конструктором). Только тогда объект будет готов .

5. Короче говоря: генеративные конструкторы не создают объекты. new Оператор в генеративном конструкторе создает объект, затем генеративные конструкторы инициализируют объект, причем каждый суперкласс получает свою очередь для инициализации своих собственных полей. Вот почему должна быть цепочка порождающих (инициализирующих) конструкторов вплоть до Object .

Ответ №3:

Одна из основных целей factory конструктора — разрешить возврат существующего экземпляра. Это в принципе несовместимо с конструктором производного класса, который должен создать новый объект. (Я полагаю, что это не обязательно должно быть так; вы могли бы представить себе дизайн, в котором два производных экземпляра фактически наследуются от общего экземпляра базового класса. Однако это было бы большой дополнительной работой для чего-то малопригодного, и это могло бы очень сбить с толку.)

Кроме того, одна из других основных целей factory конструктора заключается именно в том, чтобы предотвратить создание подкласса класса, сделав все factory неконструкторы закрытыми.