Lesson 2 · 2 of 20
Named and factory constructors · Dart
Build Dart objects in several ways: named, redirecting and private constructors, plus factory constructors that validate input or reuse objects.

A Dart class can have several constructors; the ones besides the main one are named constructors, written Class.name. Currency._ is one of them and, because it starts with an underscore, it is private: only this file can call it. It is the one that really builds objects, and it prints (new Currency ...) each time so the output shows when one is created.
Currency.euro() is a redirecting constructor: after the colon, this._('EUR', 2) hands the work to another constructor of the same class. A regular constructor, named or not, always creates a new object, which is why Currency.euro() prints (new Currency EUR).
A `factory` constructor does not have to create anything: it is a function that returns an instance, new or existing. Currency(String code) looks in the static _cache map with putIfAbsent and only builds the currency the first time it is asked for. That is why Currency('USD') and Currency.parse(' usd ') return the same object, as identical confirms, and USD is created only once.
A factory is also the place to validate or convert before building. Currency.parse cleans up the text, throws a FormatException if the code is not three letters long and, if it is fine, delegates to the main factory. Finally, format respects each currency's decimals: the yen (JPY) has none, so 1500 is printed as it is.
Code · main.dart
class Currency { final String code; final int decimals; static final Map<String, Currency> _cache = {}; // Private named constructor: the one that really builds objects. Currency._(this.code, this.decimals) { print(' (new Currency $code)'); } // Named constructor that redirects to another one. Currency.euro() : this._('EUR', 2); // A factory may return an existing object instead of a new one. factory Currency(String code) => _cache.putIfAbsent(code, () => Currency._(code, code == 'JPY' ? 0 : 2)); // A factory can also validate before building anything. factory Currency.parse(String text) { final code = text.trim().toUpperCase(); if (code.length != 3) throw FormatException('Bad currency code "$text"'); return Currency(code); } String format(int units) => decimals == 0 ? '$units $code' : '${(units / 100).toStringAsFixed(decimals)} $code';}void main() { final dollar = Currency('USD'); print('Same object: ${identical(dollar, Currency.parse(' usd '))}'); print(Currency('JPY').format(1500)); print(Currency.euro().format(1999)); try { Currency.parse('euro'); } on FormatException catch (e) { print(e.message); }}Output · dart run main.dart
(new Currency USD) Same object: true (new Currency JPY) 1500 JPY (new Currency EUR) 19.99 EUR Bad currency code "euro"
Key points
- A class can have as many named constructors as it needs.
: this(...)redirects to another constructor of the same class.- A
factorycan return an existing object, from a cache for instance. - A private constructor (
Class._) forces callers through the factories. identicalchecks whether two variables point to the same object.
Exercise
Add a Currency.fromSymbol(String symbol) factory that turns '€' into EUR and '£' into GBP, and throws a FormatException for any other symbol.