In Dart, why does a class use named constructors instead of overloaded ones, and what does a redirecting constructor add?
answer
- one name per constructor
- no overloading in Dart
- ClassName.name(...)
- colon this(...), empty body
- constructors are not inherited
basics
~20 sDart has no overloading, so a second constructor needs its own name, such as Money.euros. A redirecting constructor forwards to another constructor of the same class with : this(...), so all initialisation lives in one place.
solid answer
~40 sDart does not overload functions or constructors by parameter list, so a class gets one unnamed constructor and any number of **named constructors**, written `ClassName.name(...)`, like `Money.euros(int cents)`. The name documents intent and makes call sites read well. To avoid repeating initialisation, a named constructor can **redirect** to another generative constructor of the same class with `: this(...)` or `: this.other(...)`. A redirecting constructor has no body, no field initializers, no `assert` and no `super` call — the target does all of that. Two related facts: constructors, named ones included, are **not inherited**, so a subclass declares its own; and a class that declares no constructor gets an implicit unnamed no-argument one.
code
dart · 9 linesvoid main() {
const price = Money.euros(420);
const nothing = Money.zero('USD');
print('${price.cents} ${price.currency}'); // 420 EUR
print('${nothing.cents} ${nothing.currency}'); // 0 USD
final prices = [100, 250].map(Money.euros).toList();
print(prices.length); // 2
}go deeper
Recall that Dart has no overloading, so extra constructors are named, and that : this(...) redirects to another one.
Explain the redirect rules, why constructors are not inherited, and when the implicit default constructor exists.
Keep validation in one target constructor that every named path redirects to, and use private named constructors to control creation.
Treat the set of public constructors as API surface: each named constructor is a commitment callers will depend on.
## No overloading, so constructors get names Many languages let a class declare several constructors with the same name and different parameter lists. **Dart does not support overloading** — neither for methods nor for constructors. A class therefore has: - at most one **unnamed constructor**, `Money(...)`; - any number of **named constructors**, `Money.euros(...)`, `Money.zero(...)`, `Money._(...)`. If a class declares no constructor at all, Dart supplies a **default constructor**: unnamed, no parameters, generative. Declaring any constructor removes it. Named constructors are usually a readability win: `Money.euros(420)` says more than `Money(420, 'EUR')`, and a leading underscore (`Money._`) makes a constructor **library-private**. ## Redirecting generative constructors When several constructors share setup, a **redirecting constructor** delegates to another generative constructor of the same class: ```dart class Money { const Money(this.cents, this.currency) : assert(cents >= 0), assert(currency.length == 3); const Money.euros(int cents) : this(cents, 'EUR'); const Money.zero(String currency) : this(0, currency); final int cents; final String currency; } ``` The rules the analyzer enforces: 1. The redirect is the **only** thing after the colon: `: this(...)` or `: this.name(...)`. 2. **No body** (`redirecting_constructor_with_body`). 3. **No field initializers** (`field_initializer_redirecting_constructor`), **no `assert`** (`assert_in_redirecting_constructor`) and **no `super(...)`** (`super_in_redirecting_constructor`). 4. The target must be **generative**; redirecting a generative constructor to a factory is an error. 5. Redirect **cycles** are rejected (`recursive_constructor_redirect`). The payoff is that validation and field assignment live in exactly one constructor. In the example, the `assert`s in `Money(...)` protect every path, including `Money.euros` and `Money.zero`. A `const` redirecting constructor must target a `const` constructor. ## Redirecting factory constructors A **factory** can redirect too, with `=` instead of a colon: `factory Money.fromMinor(int cents, String currency) = Money;`. It forwards to a constructor — possibly of another class — without repeating the parameters. That is how an abstract class exposes a constructor that produces a hidden implementation. | Form | Syntax | Target | |---|---|---| | Redirecting generative | `Money.euros(int c) : this(c, 'EUR');` | generative constructor of the same class | | Redirecting factory | `factory A.make() = B;` | constructor of the class or a subtype | | Plain factory | `factory Money.parse(String s) { ... }` | any code that returns an instance | ## Constructors are not inherited A subclass inherits methods and fields, but **not constructors**. If `class Tip extends Money` declares nothing, its only constructor is its own implicit `Tip()`, which calls the superclass's unnamed no-argument generative constructor — and fails to compile if `Money` has no such constructor. To offer `Tip.euros(...)`, the subclass declares it and chains to a `Money` generative constructor with `super`. ## Tear-offs Named and unnamed constructors can be passed around as functions without calling them: `amounts.map(Money.euros)` for a named one and `Money.new` for the unnamed one. A tear-off *is* the constructor, not a lambda wrapping it. ## Choosing between the forms For a value type such as `Money`, a common shape is: - one **target constructor** that owns field assignment and every `assert` — often unnamed and `const`; - **named redirecting constructors** for convenient variants (`Money.euros`, `Money.zero`) that add no logic of their own; - **factory constructors** (`Money.parse`) for anything that needs statements, caching or a subtype; - a **private named constructor** (`Money._`) when creation must go through factories only. That split keeps each rule in one place: redirects stay one line long, validation cannot be bypassed by a forgotten path, and the class's public constructor list reads as a menu of intentions rather than a set of parameter permutations.
- In Dart, how do you pass the unnamed constructor of Money as a function?Use the tear-off `Money.new`; a named constructor tears off as `Money.euros`. The result is a function with the constructor's parameters, so `amounts.map(Money.euros)` builds one instance per element. Dart style prefers the tear-off to a lambda like `(c) => Money.euros(c)`, because the tear-off is the constructor itself.
- In Dart, why can't a redirecting constructor also validate its argument with assert?A redirecting generative constructor may contain only the redirect; the analyzer reports `assert_in_redirecting_constructor` otherwise. Put the `assert` in the target constructor's initializer list instead, where it guards every path that redirects to it. If a redirecting path needs its own checks, turn it into a factory that validates and then calls the target.
saying these in an interview costs you the question
- Dart picks between two Money constructors by their parameter types
- A subclass automatically inherits the named constructors of its superclass
- A redirecting constructor can add its own asserts and a body
- A class always keeps its default constructor after declaring named ones
- A generative constructor can redirect to a factory constructor