Что такое конструктор
Конструктор — это специальная функция для создания экземпляров класса, обычно предназначенная для инициализации свойств. Кроме конструктора по умолчанию все конструкторы имеют то же имя, что и класс.
Типы конструкторов в Dart:
- Генеративный конструктор (Generative)
- Конструктор по умолчанию (Default)
- Именованный конструктор (Named)
- Константный конструктор (Const)
- Перенаправляющий конструктор (Redirecting)
- Фабричный конструктор (Factory)
- Перенаправляющий фабричный конструктор
- Сокращенный синтаксис конструктора (Dart 3.13+)
- Основной конструктор (Primary Constructors, компактные поля + синтаксис конструктора)
1. Генеративный конструктор Generative Constructor
Базовый конструктор, создаёт экземпляры и инициализирует членские переменные. Поддерживает инициализирующие формальные параметры this.переменная для упрощения присваивания.
class Point {
double x;
double y;
// Генеративный конструктор, this.x напрямую принимает параметр для присваивания члену
Point(this.x, this.y); // Сразу присваивает значения x и y
}
void main() {
final p = Point(10, 20);
print("(${p.x}, ${p.y})"); // (10, 20)
}
Code language: JavaScript (javascript)
2. Конструктор по умолчанию Default Constructor
Если в классе не определён вручную ни один конструктор, Dart автоматически предоставляет безымянный генеративный конструктор без параметров. Поведение совпадает с другими C-подобными языками: C++, Java, C# и т.д.
class PointA {
double x = 0;
double y = 0;
// Неявный конструктор по умолчанию: PointA();
}
void main() {
final p = PointA(); // Автоматический вызов конструктора по умолчанию
print("(${p.x}, ${p.y})"); // (0.0, 0.0)
}
Code language: PHP (php)
Важно: как только вы напишете любой конструктор вручную, конструктор по умолчанию исчезнет.
3. Именованный конструктор Named Constructor
Назначение: определить несколько конструкторов в одном классе с понятной семантикой; синтаксис ИмяКласса.ИмяКонструктора()
const double xOrigin = 0;
const double yOrigin = 0;
class Point {
final double x;
final double y;
// Основной генеративный конструктор
Point(this.x, this.y);
// Именованный конструктор: начало координат
Point.origin() : x = xOrigin, y = yOrigin;
}
void main() {
final p1 = Point(5, 6);
final p2 = Point.origin(); // Вызов именованного конструктора
print(p1.x); //5
print("Начало координат: (${p2.x}, ${p2.y})"); // (0,0)
}
Code language: PHP (php)
Ключевые правила
- Подклассы не наследуют именованные конструкторы родительского класса; если нужен аналогичный именованный конструктор в подклассе — его нужно реализовать вручную;
- Именованные конструкторы поддерживают список инициализации для присваивания значений final-переменным.
4. Константный конструктор Const Constructor
Используется для создания констант времени компиляции, требования:
- Все членские переменные помечены модификатором
final - Перед конструктором указать ключевое слово
const. При создании экземпляра сconstиспользуется кэш констант, без const создаётся отдельный обычный объект.
class ImmutablePoint {
static const ImmutablePoint origin = ImmutablePoint(0, 0); // Статический общий объект для всего класса
final double x, y;
// Константный конструктор инициализирует final x и y
const ImmutablePoint(this.x, this.y);
}
void main() {
// Константа компиляции, повторно используется тот же объект
const p1 = ImmutablePoint(1, 2);
const p2 = ImmutablePoint(1, 2); // При одинаковых параметрах экземпляр общий
print(p1 == p2); // true
// Обычный экземпляр, новый отдельный объект
final p3 = ImmutablePoint(1, 2);
print(p1 == p3); // false
}Code language: PHP (php)
5. Перенаправляющий конструктор Redirecting Constructor
Конструктор, передающий управление другому конструктору того же класса через синтаксис : this(параметры). Тело функции должно быть пустым.
class Point {
double x, y;
// Основной конструктор
Point(this.x, this.y);
// Перенаправляющий конструктор: принимает только x, y фиксирован как 0, перенаправляет на основной конструктор
Point.alongX(double x) : this(x, 0); // Вызывает именованный конструктор, передаёт x основной конструкту и фиксирует y=0
}
void main() {
final p = Point.alongX(9);
print("x=${p.x}, y=${p.y}"); // x=9, y=0
}
Code language: JavaScript (javascript)
6. Фабричный конструктор Factory Constructor
Отмечен ключевым словом factory, сценарии применения:
- Не обязательно создаёт новый объект (повторное использование кэша, возврат экземпляров подклассов)
- Сложная логика перед созданием объекта (парсинг JSON, проверка параметров). Ограничение: нельзя обращаться к
this
Пример синглтон-логгера с кэшем
class Logger {
final String name;
bool mute = false;
// Приватный кэш, виден только внутри библиотеки
static final Map<String, Logger> _cache = {};
// Фабричный конструктор: сначала берёт из кэша
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
// Фабричный конструктор: создание из разобранного JSON
factory Logger.fromJson(Map<String, dynamic> json) {
return Logger(json["name"].toString());
}
// Внутренний приватный генеративный конструктор
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
void main() {
final log1 = Logger("UI");
final log2 = Logger("UI");
print(log1 == log2); // true, один объект из кэша
final jsonLog = Logger.fromJson({"name": "Net"});
jsonLog.log("request success");
}
Code language: JavaScript (javascript)
7. Перенаправляющий фабричный конструктор Redirecting Factory
Синтаксис factory Имя() = КонструкторДругогоКласса;. Подходит для абстрактных классов, делегирующих создание экземпляров, избавляет от повторного описания параметров.
abstract class Listenable {}
class _MergingListenable implements Listenable {
List<_MergingListenable> list;
_MergingListenable(this.list);
}
// Перенаправляющая фабрика, вызывает конструктор внутреннего приватного класса
factory Listenable.merge(List<Listenable> list) = _MergingListenable;
Code language: PHP (php)
8. Отделение конструкторов Constructor Tear-offs
Передача конструктора напрямую как аргумента функции без скобок (). Заменяет лишние анонимные лямбда-функции, улучшает производительность.
Рекомендуемый синтаксис
void main() {
final charCodes = [65, 66, 67];
// Именованный конструктор String.fromCharCode
final strs = charCodes.map(String.fromCharCode);
print(strs); // (A, B, C)
// Отделение безымянного конструктора через .new
final buffers = charCodes.map(StringBuffer.new);
}
Code language: PHP (php)
Не рекомендуется: лишняя лямбда
// Лишняя обёртка, не используйте
charCodes.map((code) => String.fromCharCode(code));
Code language: JavaScript (javascript)
9. Сокращённый синтаксис конструктора (Dart 3.13+) Concise Constructor
Внутри класса можно опустить имя класса, сразу использовать new / factory для определения конструкторов без записи ИмяКласса.xxx:
| Традиционный синтаксис | Сокращение |
|---|---|
Point(this.x,this.y) | new(this.x,this.y) |
Point.origin():x=0,y=0 | new origin():x=0,y=0 |
factory Point.clone() | factory clone() |
class Point {
double x, y;
// Сокращённый безымянный генеративный конструктор
new(this.x, this.y);
// Сокращённый именованный генеративный конструктор
new origin() : x = 0, y = 0;
// Сокращённый фабричный конструктор
factory clone(Point other) => new(other.x, other.y);
}
void main() {
final p = new(3, 4);
final origin = new origin();
final copy = p.clone(p);
}
Code language: PHP (php)
10. Три способа инициализации экземплярных переменных
1: Прямое присваивание при объявлении
class PointA {
double x = 1.0;
double y = 2.0;
}
Code language: JavaScript (javascript)
2: Инициализирующие параметры
this.переменная
Поддерживает позиционные опциональные параметры, именованные параметры и nullable-переменные
class PointB {
final double x;
final double y;
// Позиционные опциональные параметры
PointB.opt([this.x = 0, this.y = 0]);
// Именованные параметры со значением по умолчанию
PointB.named({this.x = 5, this.y = 5});
}
Code language: JavaScript (javascript)
3: Список инициализации Initializer List
Присваивание после двоеточия :, выполняется раньше тела конструктора, нельзя обращаться к this, поддерживает assert для проверки параметров.
class Point {
final double x;
final double y;
Point.fromJson(Map<String, double> json)
: x = json["x"]!,
y = json["y"]! {
print("Выполнено тело конструктора, x=$x");
}
// С проверкой через assert
Point.withAssert(this.x, this.y) : assert(x >= 0);
}
Code language: JavaScript (javascript)
11. Прямые инициализирующие параметры для приватных полей (Dart3.12+)
Для приватных переменных с префиксом подчёркивания параметры конструктора записываются как this._поле. При внешнем вызове используется публичное имя без подчёркивания.
class Point {
final double _x;
final double _y;
// Внутри используется _x, при вызове снаружи пишут x:10
Point({required this._x, this._y = 0});
}
void main() {
final p = Point(x: 10); // Не нужно писать _x, достаточно x
print(p._x); // 10
}
Code language: PHP (php)
В списке инициализации используйте исходное имя приватной переменной:
class Point {
final double _x;
Point({required this._x}) : assert(_x > 0);
}
Code language: JavaScript (javascript)
12. Конструкторы родительского класса и правила наследования
- Подклассы не наследуют никакие конструкторы родительского класса;
- Порядок выполнения конструктора: список инициализации → конструктор родителя → тело конструктора подкласса;
- Если у родительского класса нет безпараметрического конструктора по умолчанию, подкласс должен вручную вызвать
super()для указания родительского конструктора.
Простой пример вызова super
Как и в Java, для ссылки на базовый класс используется super, в C# — base.
class Person {
final String name;
Person(this.name);
Person.fromMap(Map map) : name = map["name"];
}
class Employee extends Person {
int id;
// Вызов именованного конструктора родителя
Employee(Map data) : super.fromMap(data), id = data["id"];
}
Code language: JavaScript (javascript)
Важно: в выражениях параметров super нельзя использовать
this.
Параметры Super (Dart2.17+, упрощают передачу аргументов родителю)
Используйте super.переменная для прямой передачи параметров родительскому конструктору без ручного написания super(x,y)
class Vector2d {
final double x, y;
Vector2d(this.x, this.y);
}
class Vector3d extends Vector2d {
final double z;
// super.x super.y автоматически передаются родительскому конструкту
Vector3d(super.x, super.y, this.z);
}
// Именованный конструктор родителя + именованные super-параметры
class Vector3dYZ extends Vector2d {
final double z;
Vector3dYZ({required super.y, required this.z}) : super.named(x: 0);
}
Code language: JavaScript (javascript)