Классы и ООП в Dart
:::tip Сначала — общие понятия (раздел 4 «Код») Если ООП для вас новое или вы учите Dart с нуля, сначала пройдите материалы без привязки к синтаксису: парадигмы и уровни абстракции, затем ООП — о разделе — зачем объекты, введение, абстракция, инкапсуляция, наследование, полиморфизм. Ниже — классы, миксины и интерфейсы в Dart. :::
Классы и ООП в Dart
Dart — объектно-ориентированный язык: каждое значение — объект, корень иерархии — Object. Базовый синтаксис классов уже разобран в основах; здесь — механизмы, которые отличают Dart от «классического» ООП и важны в реальных проектах и во Flutter.
Связанные темы: типы и record, паттерны и sealed, функции и методы.
Поля, конструкторы и this
Класс объединяет состояние (поля) и поведение (методы):
class User {
final String id;
String displayName;
User(this.id, this.displayName);
void rename(String name) {
displayName = name;
}
}
Синтаксис User(this.id, this.displayName) — сокращение: параметры конструктора сразу инициализируют одноимённые поля. Именованные параметры в конструкторе повышают читаемость:
class Config {
final String host;
final int port;
Config({required this.host, this.port = 8080});
}
Инкапсуляция — библиотека и префикс _
В Dart нет ключевых слов public / private на уровне члена. Приватность — по области видимости библиотеки (файла): имя, начинающееся с _, видно только внутри того же файла (или части library, если несколько файлов объединены директивой part).
class Account {
final String _id;
double _balance = 0;
Account(this._id);
void deposit(double amount) {
if (amount <= 0) throw ArgumentError.value(amount);
_balance += amount;
}
double get balance => _balance;
}
Снаружи файла к _balance не обратиться — только через публичный API (deposit, balance). Это защищает инварианты объекта без boilerplate геттеров на каждое поле.
late — отложенная инициализация
Ключевое слово late сообщает компилятору: поле будет присвоено до первого чтения, но не обязательно в конструкторе.
class Report {
late final String title;
void load(Map<String, dynamic> json) {
title = json['title'] as String;
}
}
Варианты:
late final— присвоить один раз, затем только читать.lateбез final — изменяемое поле с отложенным стартом.
Ошибка LateInitializationError возникает, если прочитать late-поле до инициализации. В Flutter late часто применяют для контроллеров (TextEditingController), которые нельзя создать в конструкторе виджета до initState.
Не путать с nullable (String?): late обещает «значение появится позже, но не null», ? — «может быть null».
final, const и неизменяемые объекты
finalна поле — ссылку переназначить нельзя; для изменяемых коллекций содержимое всё ещё можно менять, если тип коллекции изменяемый.constконструктор — объект создаётся на этапе компиляции и может быть каноническим (один экземпляр на одинаковые аргументы).
class Point {
final double x;
final double y;
const Point(this.x, this.y);
}
const origin = Point(0, 0);
Все поля класса с const-конструктором должны быть final и тоже допускать const-инициализацию. Во Flutter const виджеты уменьшают пересборки дерева — см. Flutter.
Factory-конструкторы
Обычный конструктор всегда создаёт новый объект. factory может вернуть существующий экземпляр или подтип:
class Logger {
static final Map<String, Logger> _cache = {};
final String name;
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
Logger._internal(this.name);
}
Типичные случаи factory:
- кэш и singleton;
- выбор реализации по аргументу (
factory Auth.fromConfig(...)→OAuthилиBasic); - парсинг:
factory User.fromJson(Map<String, dynamic> json).
Именованный redirecting factory перенаправляет на другой конструктор:
factory Vector.zero() = Vector.fromValues;
Наследование и абстрактные классы
Один класс наследует один суперкласс (extends). Переопределение методов помечают @override:
abstract class Shape {
double area();
void describe() => print('фигура, площадь $area');
}
class Circle extends Shape {
final double radius;
Circle(this.radius);
@override
double area() => 3.14159 * radius * radius;
}
abstract class нельзя инстанцировать; методы без тела задают контракт. abstract interface class (Dart 3) — вариант для чистого контракта без реализации по умолчанию.
Интерфейсы без ключевого слова interface
Любой класс задаёт неявный интерфейс — набор публичных методов. Реализация через implements (нужно описать все методы заново) или extends (наследование реализации):
abstract class Serializable {
Map<String, dynamic> toJson();
}
class Product implements Serializable {
final String sku;
Product(this.sku);
@override
Map<String, dynamic> toJson() => {'sku': sku};
}
implements удобен, когда класс уже наследует другой базовый класс, но должен подчиняться второму контракту.
Миксины (mixins)
mixin добавляет поведение без цепочки наследования «от животного к млекопитающему». Подключение — with:
mixin Timestamped {
DateTime? updatedAt;
void touch() => updatedAt = DateTime.now();
}
class Document with Timestamped {
final String title;
Document(this.title);
}
Ограничение on задаёт, к каким классам можно примешивать mixin:
mixin Pilot on Vehicle {
void fly() => print('взлёт');
}
Миксины — основа композиции в Dart; не злоупотребляйте глубокими иерархиями extends.
Геттеры, сеттеры и операторы
Свойства могут быть вычисляемыми:
class Rectangle {
final double width, height;
Rectangle(this.width, this.height);
double get area => width * height;
set scale(double factor) {
// в реальном классе поля должны быть изменяемыми или через внутреннее состояние
}
}
Операторы переопределяют через operator:
class Vector {
final int x, y;
const Vector(this.x, this.y);
Vector operator +(Vector other) => Vector(x + other.x, y + other.y);
}
Не забывайте согласованно переопределять == и hashCode, если объекты сравнивают по значению (как для коллекций в Map/Set).
Sealed и final class (Dart 3)
sealed class — закрытая иерархия для исчерпывающего switch. final class запрещает наследование снаружи файла. base / interface — тонкая настройка, кто может расширять или реализовывать тип. В прикладном коде чаще всего встречаются sealed для моделей состояния и final для «листьев» иерархии.
Область видимости
Помимо _ на уровне библиотеки:
- топ-уровневые функции и переменные в файле — видны всему файлу;
- локальные — внутри блока
if, цикла, метода; - статические члены класса —
static, принадлежат типу, не экземпляру.
Вложенные функции (как в статье про функции) видят переменные внешней функции — основа замыканий.
ООП и исключения
Методы бросают исключения через throw; перехват — try/catch. Собственный тип:
class ValidationException implements Exception {
final String field;
ValidationException(this.field);
@override
String toString() => 'ошибка поля $field';
}
Dart не объявляет checked exceptions в сигнатуре — контракт ошибок документируют в комментариях и тестах.
Сводка — что запомнить
| Механизм | Зачем |
|---|---|
_ | Скрыть детали реализации в файле |
late | Инициализация после конструктора |
const / final | Предсказуемость и производительность |
factory | Кэш, подтипы, fromJson |
abstract | Контракт без экземпляра |
implements / with | Контракт и миксины без лишнего наследования |
sealed | Закрытый набор вариантов для switch |
Дальше: паттерны для разбора вариантов данных, консоль и HTTP для сервисного слоя, чек-лист для самопроверки.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). История Dart - возникновение языка в Google и его развитие как основы для Flutter-экосистемы. Простые приложения на Dart — CLI, файлы, JSON и минимальный HTTP на dart:io. Dart выглядит как Java или C#, но есть свои привычки: точка с запятой в конце строки не обязательна, строки в "...", многострочные литералы '''. Основы Dart - точка входа `main`, базовый синтаксис и старт построения приложений на языке. Runtime Dart — система типов, isolates, event loop, JIT/AOT, сборка мусора, SDK и связка с Flutter. Flutter - экосистема Dart для кроссплатформенной UI-разработки, сборки и тестирования приложений. Типизация, набор правил определения типа данных значений языка. Условия, циклы, switch, арифметика, логика и null-aware операторы Dart — с примерами для Dart 3. Функция — это именованный фрагмент кода, который принимает входные данные, выполняет определённую последовательность действий и может возвращать результат. Гайд по установке и настройке с написанием первой программы и её запуском. Switch expressions, сопоставление с образцом для record, списков, Map и sealed-типов — исчерпывающий разбор вариантов в Dart 3. Консольный ввод-вывод, работа с файлами, JSON и базовый HTTP-клиент и сервер через dart:io — Dart вне Flutter.История языка Dart
Простые приложения на Dart
Синтаксис и пунктуация в Dart
Основы языка Dart
Архитектура платформы Dart
Flutter
Типы данных и безопасность типов
Управляющие конструкции и операторы Dart
Функции и асинхронность
Первая программа на Dart
Паттерны и switch в Dart 3
Консоль, файлы и HTTP в Dart