Какие типы конструкторов существуют в Dart?

Ответ

В Dart есть несколько типов конструкторов, каждый решает свою задачу:

1. Стандартный конструктор:

class Point {
  double x, y;
  Point(this.x, this.y); // Сокращённая инициализация
}

2. Именованный конструктор (named constructor): Позволяет создавать объекты альтернативными способами.

class Point {
  double x, y;
  Point(this.x, this.y);

  Point.origin() : x = 0, y = 0; // Именованный конструктор
  Point.fromJson(Map<String, double> json) : x = json['x']!, y = json['y']!;
}

3. Конструктор с перенаправлением (redirecting constructor): Перенаправляет вызов другому конструктору того же класса.

class Point {
  double x, y;
  Point(this.x, this.y);
  Point.alongXAxis(double x) : this(x, 0); // Вызывает Point(x, 0)
}

4. Фабричный конструктор (factory constructor): Используется, когда создание экземпляра требует логики (кеширование, возврат подтипа).

class Logger {
  final String name;
  static final Map<String, Logger> _cache = {};

  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }
  Logger._internal(this.name); // Приватный конструктор
}

5. Константный конструктор (const constructor): Создаёт compile-time константы, все поля должны быть final.

class ImmutablePoint {
  final double x, y;
  const ImmutablePoint(this.x, this.y);
}
// Использование:
const p1 = ImmutablePoint(1, 2);
const p2 = ImmutablePoint(1, 2);
print(identical(p1, p2)); // true — это один и тот же объект в памяти

Ответ 18+ 🔞

Э, слушай, а вот Dart, он, блядь, такой разносторонний, что конструкторов у него — овердохуища разных видов. Прям как инструменты в гараже: каждый для своего дела, а если перепутаешь — получишь пиздопроебибну в коде. Сейчас разжую, как есть.

1. Обычный, как две копейки.
Ну тут всё просто, ёпта. Создал класс — написал конструктор. Dart ещё любезно позволяет сократить эту писанину через this. — красота же!

class Point {
  double x, y;
  Point(this.x, this.y); // Всё, готово, нехуй тут философствовать
}

2. Именованный — когда одного способа создания мало.
Бывает же, что объект можно собрать из разных источников? Вот для этого они и нужны. Назвал как хочешь — и вперёд.

class Point {
  double x, y;
  Point(this.x, this.y);

  Point.origin() : x = 0, y = 0; // Нулевая точка, классика
  Point.fromJson(Map<String, double> json) : x = json['x']!, y = json['y']!; // Из JSON, модно-молодёжно
}

3. Перенаправляющий — ленивая жопа.
Зачем писать одну и ту же логику дважды, если можно просто ткнуть пальцем в другой конструктор и сказать: «Сам делай!». Экономия времени, ядрёна вошь.

class Point {
  double x, y;
  Point(this.x, this.y);
  Point.alongXAxis(double x) : this(x, 0); // По сути, кричим: «Эй, Point(x, 0), сделай за меня работу!»
}

4. Фабричный — хитрая жопа.
Вот это уже интереснее. Тут тебе и кеширование, и выбор подтипа, и вообще любая логика перед созданием объекта. Но будь осторожен, а то доверия ебать ноль к таким штукам — легко накосячить.

class Logger {
  final String name;
  static final Map<String, Logger> _cache = {};

  factory Logger(String name) {
    // Смотри, не создаём новый логгер, если уже есть с таким именем — умно, да?
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }
  Logger._internal(this.name); // А это приватный конструктор, чтоб с улицы не дёргали
}

5. Константный — для перфекционистов.
Если все поля final и объект как с конвейера — одинаковый, можно сделать его константным. Тогда два одинаковых вызова вернут один и тот же объект в памяти, а не копии. Экономия, блядь, на уровне компилятора!

class ImmutablePoint {
  final double x, y;
  const ImmutablePoint(this.x, this.y);
}
// Использование:
const p1 = ImmutablePoint(1, 2);
const p2 = ImmutablePoint(1, 2);
print(identical(p1, p2)); // true — один объект, две ссылки. Красота!

Вот и вся магия. Главное — не путать, когда какой юзать, а то получится хуй в пальто, а не код. Удачи, чувак!