Что такое redirecting constructor в Dart?

Ответ

Redirecting constructor (перенаправляющий конструктор) в Dart — это конструктор, который не имеет собственного тела и списка инициализаторов, а лишь перенаправляет вызов на другой конструктор того же класса с помощью синтаксиса : this(...). Это позволяет создавать удобные фабричные методы или альтернативные способы инстанцирования объекта без дублирования кода.

Синтаксис и пример:

class Rectangle {
  final double width;
  final double height;
  final String label;

  // Основной конструктор
  Rectangle(this.width, this.height, [this.label = 'Rectangle']);

  // Redirecting конструктор для создания квадрата.
  // Он просто вызывает основной конструктор, подставляя height = width.
  Rectangle.square(double sideLength) : this(sideLength, sideLength, 'Square');

  // Ещё один redirecting конструктор с преобразованием параметров.
  Rectangle.fromMap(Map<String, double> dimensions)
      : this(dimensions['width'] ?? 0.0, dimensions['height'] ?? 0.0);
}

void main() {
  final rect = Rectangle(10, 20);
  final square = Rectangle.square(15); // Вызовет Rectangle(15, 15, 'Square')
  final fromMap = Rectangle.fromMap({'width': 5, 'height': 7});
}

Ключевые особенности:

  1. Нет тела: После двоеточия (:) может быть только вызов this(...).
  2. Нет initializer list: Нельзя использовать : width = 10, height = 20.
  3. Цель — избежать дублирования: Вся логика инициализации сосредоточена в основном конструкторе.
  4. Удобство для API: Позволяет предоставить пользователю класса более интуитивно понятные имена методов создания (например, Point.polar(radius, angle)).

Ответ 18+ 🔞

Ёпта, слушай, вот есть у тебя класс, как будто чертёж для создания штук. И конструктор — это такой спецметод, который из этого чертежа реальную хрень делает. Так вот, иногда хочется сделать штуку по-разному, но чтобы внутри всё равно собиралось одним проверенным способом, без копипасты кода, который потом поддерживать — пиздец.

Вот смотри, представь, что у тебя класс Rectangle — прямоугольник. У него есть ширина, высота и, допустим, какая-нибудь подпись.

class Rectangle {
  final double width;
  final double height;
  final String label;

  // Вот это наш главный, основной конструктор. Он всё умеет.
  Rectangle(this.width, this.height, [this.label = 'Rectangle']);
}

Всё чётко. Но потом тебе в голову приходит: «А дай-ка я сделаю удобный способ создавать квадраты!». Квадрат — это же прямоугольник, у которого ширина и высота одинаковые. И терпения ноль ебать писать каждый раз Rectangle(15, 15, 'Square').

Можно, конечно, накостылить отдельный метод, но зачем, если в Dart есть redirecting constructor — перенаправляющий конструктор. Это такая хитрая жопа, у которой нет своего тела. Вообще. Она только говорит: «Эй, чувак, иди нахуй, вернись к главному конструктору и скажи ему собрать объект вот с такими параметрами».

Вот как это выглядит:

class Rectangle {
  final double width;
  final double height;
  final String label;

  Rectangle(this.width, this.height, [this.label = 'Rectangle']);

  // А вот и он! Redirecting constructor для квадрата.
  // Видишь двоеточие и this(...)? Всё. Больше ничего.
  // Он берёт sideLength и говорит главному конструктору:
  // «Собери прямоугольник, где width = sideLength, height = sideLength, label = 'Square'».
  Rectangle.square(double sideLength) : this(sideLength, sideLength, 'Square');
}

И теперь в коде просто пишешь:

final mySquare = Rectangle.square(10); // Вжух! И создался Rectangle(10, 10, 'Square')

Ещё пример, чтобы вообще всё стало ясно, как божий день. Допустим, тебе данные приходят в виде мапы (JSON там какой-нибудь). Тоже не хочется руками распаковывать:

class Rectangle {
  // ... поля те же ...

  Rectangle(this.width, this.height, [this.label = 'Rectangle']);

  Rectangle.square(double sideLength) : this(sideLength, sideLength, 'Square');

  // И ещё один redirecting constructor! Из мапы.
  // Берёт мапу, выковыривает оттуда 'width' и 'height' (или 0.0, если нихуя нет),
  // и — бац! — передаёт это всё в основной конструктор.
  Rectangle.fromMap(Map<String, double> dimensions)
      : this(dimensions['width'] ?? 0.0, dimensions['height'] ?? 0.0);
}

А теперь главные правила, чтобы не обосраться:

  1. Никакого тела. После : this(...) точка с запятой и всё. Нельзя написать { print('hello'); } — будет тебе хиросима и нигерсраки в виде ошибки компиляции.
  2. Никакого своего списка инициализации. Только вызов this(...). Нельзя написать : width = 10, this(10, 20).
  3. Вся сила — в основном конструкторе. Redirecting-конструктор — это просто красивый фасад, дверь, которая ведёт в одну и ту же комнату. Вся настоящая работа (проверки, присваивания final-полям) делается там, куда он перенаправляет.
  4. Удобство для тех, кто будет этим пользоваться. Вместо того чтобы дергать в коде Rectangle(15, 15, 'Square'), можно дать людям понятный Rectangle.square(15). И доверия ебать больше, и в документации красивее.

Короче, redirecting constructor — это как если бы ты, вместо того чтобы самому лезть в холодильник за пивом, сказал корегу: «Слушай, принеси-ка мне из холодильника пивасика». Он делает всю грязную работу (открывает дверцу, ищет), а ты получаешь готовый результат. Удобно, ёпта.