In Dart, what can a factory constructor do that a generative constructor cannot, and what are its limits?
answer
- does not have to allocate
- cache or subtype
- logic before any instance exists
- no this, no initializer list
- cannot be a super call target
basics
~20 sA factory constructor runs arbitrary code and then returns an instance it chooses: a cached object, a subtype, or one built via another constructor. It cannot use this or an initializer list, cannot return null, and cannot be called with super.
solid answer
~40 sA generative constructor always allocates a fresh instance of exactly its class and initialises it. A `factory` constructor is closer to a static method that callers invoke with constructor syntax: it can run validation or parsing first, return an existing instance from a cache, or return an instance of a subtype, as long as the result is an instance of the class type. The limits follow from that: it has no `this` because no object is being built, no initializer list and no initializing formals, and under null safety it cannot return `null` — invalid input means throwing, or offering a static `tryParse` that returns `Money?`. A subclass cannot call it with `super`, because only generative constructors can be invoked from an initializer list.
code
dart · 14 linesvoid main() {
final a = Money.zero('EUR');
final b = Money.zero('EUR');
print(identical(a, b)); // true: the factory returned the cached instance
final price = Money.parse('4.20 EUR');
print(price.cents); // 420
try {
Money.parse('oops');
} on FormatException catch (e) {
print(e.message); // Expected "<amount> <CUR>"
}
}go deeper
Recall that factory constructors may return cached instances or subtypes and run logic first, but cannot use this or return null.
Explain why factories lack initializer lists and this, why they cannot be super targets, and when a static tryParse is the better API.
Use private generative constructors plus factories to control how instances are created, and note how that choice affects subclassing and const.
Decide which creation APIs a shared value type exposes, weighing caching, subtype freedom and extensibility against the constraints factories impose.
## Two kinds of constructor Dart splits constructors into two families: - A **generative constructor** creates a new instance of its class, runs field initializers, the initializer list, the superclass constructor and finally its body. It always yields a **fresh object of exactly that class**. - A **factory constructor**, marked with `factory`, does not create anything by itself. It is a function body that must **return** an instance of the class type. Callers cannot tell the difference: `Money.parse('4.20 EUR')` looks the same either way. ## What a factory can do The Dart documentation lists the cases for `factory`: 1. **Return an existing instance** — for example, a cached object per key. 2. **Return a subtype** — an abstract class can expose `factory Shape.circle(...)` that returns a private implementation. 3. **Do non-trivial work first** — validate or parse arguments with statements and local variables, which an initializer list cannot express. A money value class shows all three in miniature: ```dart class Money { Money._(this.cents, this.currency); final int cents; final String currency; static final Map<String, Money> _zeros = {}; factory Money.zero(String currency) => _zeros.putIfAbsent(currency, () => Money._(0, currency)); factory Money.parse(String text) { final parts = text.trim().split(' '); if (parts.length != 2) { throw FormatException('Expected "<amount> <CUR>"', text); } final cents = (double.parse(parts[0]) * 100).round(); return Money._(cents, parts[1]); } } ``` `Money.zero` hands back the same object for each currency; `Money.parse` validates input and delegates the real construction to the private generative constructor `Money._`. ## What a factory cannot do | Capability | Generative | Factory | |---|---|---| | Use `this` | in the body | never | | Initializer list, `this.x` formals | yes | no | | Return an existing object | no | yes | | Return a subtype | no | yes | | Return `null` | no | no | | Be called by a subclass via `super` | yes | no | | Be `const` | yes | only as a redirecting factory | The rows that trip candidates up: - **No `null`.** Under sound null safety the factory's result type is the non-nullable class type. For "parse or give up" semantics, add a **static method** such as `static Money? tryParse(String text)` instead. - **No `super` target.** A subclass initializer list may only invoke a *generative* superclass constructor; `super.parse(...)` against a factory is the `non_generative_constructor` error. A class whose only generative constructor is private (`Money._`) therefore cannot be extended from another library. - **No `this`.** Nothing has been allocated when the factory body runs. ## Redirecting factories A factory can also be written as a pure redirect: `factory Shape.circle(double r) = _Circle;`. It forwards the call to another class's constructor without repeating parameters or defaults, and it is the one way to give an abstract class a `const` constructor that produces a concrete implementation. ## Factory or static method? Both run code before an instance exists. Prefer a **factory** when callers should see ordinary construction syntax and always get an instance; prefer a **static method** when the operation can fail softly (`tryParse` returning `Money?`), is asynchronous (a constructor cannot be `async`), or its name reads better as a verb. ## What interviewers listen for - That a factory is **not required to allocate**, and that callers cannot tell a factory from a generative constructor at the call site. - That the missing `this`, initializer list and `super` support all follow from one fact: a factory initialises nothing. - That **null is never a valid result**, so soft failure belongs in a static method. - That the idiom of a private generative constructor behind public factories also controls who may subclass the type.
- In Dart, how do you offer a constructor-like API that returns null for invalid input?Not with a factory: its result type is the non-nullable class type, so it must return an instance or throw. Add a static method such as `static Money? tryParse(String text)` that returns `null` on bad input, and keep `factory Money.parse` for callers who want an exception.
- In Dart, why can't a subclass of Money call `super.zero('EUR')` in its initializer list?A subclass must initialise the superclass part of a new object it is allocating, which only a generative constructor can do. `Money.zero` is a factory that returns some existing or new object rather than initialising the one under construction, so the analyzer reports `non_generative_constructor`. The subclass must call a generative constructor, which it can reach only if one is accessible from its library.
saying these in an interview costs you the question
- A factory constructor can return null when the input is invalid
- A factory constructor can use this to set fields
- A factory constructor always creates a new instance, just with extra logic
- A subclass can call a superclass factory constructor with super
- Factory constructors must be static methods named create