# Classes, Constructors and Initialiser Lists — Dart

Source: https://www.skillbyai.com/en/dart/c-classes

> Write classes with generative, named, const and factory constructors.

## Constructors in many shapes

Dart classes have fields, methods, getters and setters. Constructors come in several forms. **Generative constructors** with **initialising formal parameters** assign fields concisely: `Product(this.title, this.price);`. **Named constructors** create alternatives: `Product.free(this.title) : price = 0;`. An **initialiser list** after a colon sets final fields and runs `assert`s before the body. **`const` constructors** (all fields `final`) allow compile-time constant instances, essential for efficient Flutter widgets. **`factory` constructors** can return cached instances, subclasses or instances built from data, such as `factory Product.fromJson(Map<String, dynamic> json)`. Fields marked **`final`** must be set by the time the constructor finishes, and **`late final`** fields can be set once later. **Private** members start with an underscore (`_secret`) and are private to the **library** (the file and its parts), not the class. Getters (`double get total => ...`) compute values, and Dart provides `==` and `hashCode` only by identity unless you override them, which packages such as `equatable` or code generators can help with.

## Ways to create an object

Generative, named, const and factory constructors produce instances in different ways.

![Four entry arrows from different shaped gates converging on one object shape.](assets/figures/dart/section-4-map.svg) — Figure 4.1 — Constructor kinds in Dart.

## A model class with several constructors

const, named and factory constructors with an initialiser list.

```dart
class Product {
  const Product(this.sku, this.title, this.pricePaise)
      : assert(pricePaise >= 0);

  const Product.free(this.sku, this.title) : pricePaise = 0;     // named constructor

  factory Product.fromJson(Map<String, dynamic> json) {          // factory constructor
    return Product(
      json['sku'] as String,
      json['title'] as String,
      (json['price_paise'] as num).toInt(),
    );
  }

  final String sku;
  final String title;
  final int pricePaise;

  String get displayPrice => 'Rs ${(pricePaise / 100).toStringAsFixed(2)}';   // getter

  Map<String, dynamic> toJson() => {'sku': sku, 'title': title, 'price_paise': pricePaise};

  @override
  bool operator ==(Object other) =>
      other is Product && other.sku == sku && other.pricePaise == pricePaise;

  @override
  int get hashCode => Object.hash(sku, pricePaise);
}

void main() {
  const pen = Product('PEN', 'Blue pen', 4950);
  final sample = Product.free('SAMPLE', 'Sample sachet');
  final fromApi = Product.fromJson({'sku': 'INK', 'title': 'Ink', 'price_paise': 19900});
  print(pen.displayPrice);         // Rs 49.50
}
```

## Override == and hashCode together

If two objects are equal by `==`, they must have the same `hashCode`, or sets and maps behave incorrectly. `Object.hash(...)` makes a correct implementation easy.

**Quiz:** What can a factory constructor do that a generative constructor cannot?

- [ ] Take parameters
- [ ] Initialise final fields
- [x] Return an existing cached instance or a subtype instead of always creating a new instance of the class
- [ ] Be called with new

*Answer:* Return an existing cached instance or a subtype instead of always creating a new instance of the class. Factory constructors decide what to return, including cached objects or subclasses.
