Что такое класс и объект
Класс — это категория, способ классификации. Мы абстрагируем окружающие предметы и объединяем их в единый тип. Например, все люди на планете объединяем в класс «Человек». Конкретный человек, как Боб или Том, является объектом.
Класс выступает как «чертеж» или «шаблон», который определяет структуру и поведение всех связанных с ним объектов.
Возьмем тот же пример с классом Человек: этот шаблон описывает, что есть у человека и что он может делать. У него есть имя, руки, ноги и другие органы, он может говорить и думать. Данные как имя или возраст — это свойства класса, а действия как разговор или размышление — его поведение. Иными словами, класс выделяет общие признаки из реальных объектов. По этому шаблону можно создавать отдельные объекты, например Джек или Джим.
Свойства класса (имя, возраст) и его поведение (разговор, мышление) представлены переменными и функциями соответственно.
В нем прописаны свойства (переменные) и методы (функции), которые должен иметь каждый объект.
Класс можно сравнить с формой для выпечки хлеба: сама форма не является готовым изделием. Из одной формы получается много одинаковых булок, так же из одного класса создается множество объектов.
В начале истории программирования понятия класса не существовало. Яркий пример — язык C, в котором используются только функции, переменные и операторы, такой подход называется процедурным программированием. По мере усложнения программ выявлялись недостатки этой модели: много дублирующего кода, хаотичная структура, отсутствие четкой иерархии. Позже появился объектно-ориентированный подход: логика программы абстрагируется в отдельные объекты, как в реальном мире. Сравним с фирмой: сначала руководитель сам выполняет все задачи, после найма сотрудников бухгалтерию ведет финансовый отдел, подбор персонала занимается кадровая служба. Разделение обязанностей делает работу упорядоченной.
Объект — это экземпляр класса.
При использовании ключевого слова new или конструктора класс генерирует новый объект.
Объект — это реальная сущность во время выполнения программы, способная хранить данные и вызывать методы.
Связь между классами и объектами
- Класс — это архитектурный чертеж, объект — дом, построенный по этому чертежу.
- Из одного класса можно создать множество объектов, каждый хранит собственные независимые данные.
- Все объекты, созданные из одного класса, разделяют поведение (методы), прописанные в классе.

Теперь перейдем к языку Dart
Мы разберем следующие темы
Dart — полностью объектно-ориентированный язык, любое значение является экземпляром класса. За исключением Null все классы наследуют базовый класс Object.
Наследование в Dart реализовано через комбинацию классов и миксинов (Mixin)
Одиночное наследование: каждый класс имеет только один родительский класс, но миксины позволяют переиспользовать логику из нескольких разных классов
Расширяющие методы: добавляют новые функции в класс без изменения исходного кода или создания подкласса
Модификаторы классов: регулируют, могут ли другие библиотеки наследовать или реализовывать данный класс
Начнем изучать классы в Dart
Члены класса
Члены класса объединяют методы и переменные — это все элементы, из которых состоит класс.
Оператор . используется для доступа к членам экземпляра; оператор безопасного доступа к нулю ?. предотвращает исключения нулевого указателя
Ниже приведен полный пример, демонстрирующий объявление класса, создание объекта из класса и вызов его членов по имени объекта
// Объявить класс — это как создать шаблон-форму
class Point {
double x, y;
Point(this.x, this.y);
// Метод экземпляра: вычислить квадрат расстояния между двумя точками
double distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return dx * dx + dy * dy;
}
}
void main() {
// Создать объект p из класса Point для выполнения операций
var p = Point(2, 2);
// Прочитать переменную экземпляра (неявный геттер)
print(p.y); //2.0
// Вызвать метод экземпляра
double dist = p.distanceTo(Point(4, 4));
print(dist); // 8.0
// Безопасный доступ через ?.
Point? nullablePoint;
var a = nullablePoint?.y; // Если объект равен null, в a запишется null без ошибки
print(a); // null
}Code language: JavaScript (javascript)

Конструкторы (Constructors)
Стандартные конструкторы
Dart отличается от других C-подобных языков поддержкой именованных конструкторов. В большинстве языков на основе C конструктор должен совпадать по названию с классом, а в Dart можно создавать конструкторы с идентификатором, отличным от имени класса.
Синтаксис: ИмяКласса() / ИмяКласса.Идентификатор() ;
Ключевое слово new можно опускать (поддерживается с Dart 2)
class Point {
double x, y; // Две переменные-члены для хранения свойств класса
// Стандартный конструктор: инициализировать переменные x и y
Point(this.x, this.y);
// Именованный конструктор Point.fromJson: инициализация x и y из данных JSON
Point.fromJson(Map<String, double> json)
: x = json['x']!,
y = json['y']!;
}
void main() {
var p1 = Point(2, 2);//Вызов стандартного конструктора, передача 2 и 2, x=2, y=2 для p1
var p2 = Point.fromJson({'x': 1, 'y': 2}); //Передача JSON в именованный конструктор fromJson
// new можно писать или опускать, поведение полностью одинаковое
var p3 = new Point(3, 3); //Создание объекта через стандартный конструктор
print("p1:(${p1.x},${p1.y}), p2:(${p2.x},${p2.y}), p3:(${p3.x},${p3.y})");
//Вывод: p1:(2.0,2.0), p2:(1.0,2.0), p3:(3.0,3.0)
}Code language: JavaScript (javascript)
Константные конструкторы с const
Перед конструктором добавьте ключевое слово const, чтобы создать константные объекты времени компиляции; константы с одинаковыми параметрами используют один и тот же экземпляр повторно.
class ImmutablePoint {
final double x, y;
// Правило константного конструктора: все члены помечаются модификатором final
const ImmutablePoint(this.x, this.y);
}
void main() {
// Создание константных экземпляров
var a = const ImmutablePoint(1, 1); // Ключевое слово const обязательно здесь
var b = const ImmutablePoint(1, 1);
print(identical(a, b)); // true, ссылаются на один объект
var m = const ImmutablePoint(1, 1);
var n = const ImmutablePoint(2, 1);
print(identical(m, n)); // false, разные значения создают отдельные объекты
// Без const — обычный объект, разделения экземпляров нет
var c = ImmutablePoint(1, 1);
print(identical(a, c)); //false
// В контексте const внутреннее const можно опустить
const pointMap = {
'p1': [ImmutablePoint(0, 0)],
'p2': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)]
};
print(pointMap); //{p1: [Instance of 'ImmutablePoint'], p2: [Instance of 'ImmutablePoint', Instance of 'ImmutablePoint']}
print(identical(p1, p2)); // false
print(identical(p2, p3)); // false
print(identical(p1, p3)); // false
}Code language: PHP (php)
Константный конструктор повторно использует экземпляр только при полном совпадении всех передаваемых параметров; хотя бы один разный параметр создаст два независимых объекта.
Получить тип объекта во время выполнения через runtimeType
Тип во время выполнения берется через объект.runtimeType; лучше использовать оператор проверки типа is, а не сравнения через runtimeType
class Point {
double x, y;
Point(this.x, this.y);
// Метод экземпляра: вычислить квадрат расстояния между двумя точками
double distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return dx * dx + dy * dy;
}
}
void main() {
var p = Point(2, 3);
print("Тип p: ${p.runtimeType}"); // Point
// Рекомендованный стабильный способ проверки
if (p is Point) {
print("p является экземпляром Point");
}
// Не рекомендуется для рабочего кода
if (p.runtimeType == Point) {
print("p является экземпляром Point ");
}
}Code language: JavaScript (javascript)
Переменные экземпляра Instance Variables
- Неназначенные нулевые типы по умолчанию равны
null; ненулевые типы обязательно нужно инициализировать - Все переменные экземпляра автоматически получают неявный геттер; не-final или неинициализированные late final переменные имеют автоматический сеттер
- Обычные выражения инициализации переменных экземпляра не имеют доступа к this; для late переменных доступ к this при инициализации разрешен
- Final переменные экземпляра получают значение только один раз: при объявлении, через параметр конструктора или в списке инициализации конструктора
Неявные геттеры и сеттеры
class Point {
double? x; // Начальное значение null, геттер + сеттер создаются автоматически
double? y;
double z = 0; // Инициализировано нулем
}
void main() {
var point = Point();
point.x = 4; // Вызов неявного сеттера
assert(point.x == 4); // Вызов неявного геттера
assert(point.y == null);
print(point.z); // 0
}
Code language: JavaScript (javascript)
Ограничение доступа к this в выражениях инициализации
double globalX = 1.5;
class Point {
// Разрешено: обращение к внешней глобальной переменной без this
double? x = globalX;
// Ошибка компиляции: стандартная инициализация не допускает this
// double? y = this.x;
// Разрешено: late переменные могут ссылаться на this при инициализации
late double? z = this.x;
// Разрешено: синтаксический сахар this.x в параметрах конструктора не является выражением инициализации
Point(this.x, this.y);
double? y;
}
void main() {
var p = Point(null, 5);
print(p.z); // null
}
Code language: JavaScript (javascript)
Три способа инициализации final переменных экземпляра
class ProfileMark {
// Способ 1: прямая инициализация при объявлении
final DateTime start = DateTime.now();
final String name;
// Способ 2: присваивание через параметр конструктора
ProfileMark(this.name);
// Способ 3: присваивание в списке инициализации конструктора; именованный конструктор unnamed по умолчанию записывает пустую строку в name
ProfileMark.unnamed() : name = '';
}
void main() {
var m1 = ProfileMark("user1");
var m2 = ProfileMark.unnamed();
print(m1.name);
print(m2.name);
}
Code language: PHP (php)
Неявные интерфейсы Implicit interfaces
Каждый класс Dart имеет неявный интерфейс, включающий все открытые члены экземпляра класса.
implements: реализовать интерфейс, нужно переопределить все методы интерфейсаextends: наследовать родительский класс для переиспользования его реализации- Закрытые переменные с префиксом
_xxxне экспортируются в интерфейс
1 Реализация одного интерфейса
class Person {
final String _name;
Person(this._name);
// Открытый метод интерфейса
String greet(String who) => "Hello $who, I'm $_name";
}
// Реализовать интерфейс Person: обязательно переопределить все его члены
class Impostor implements Person {
String get _name => "";
@override
String greet(String who) => "Hi $who, guess who?";
}
String greetBob(Person p) => p.greet("Bob");
void main() {
print(greetBob(Person("Kathy")));
print(greetBob(Impostor()));
}
Code language: PHP (php)
При переопределении метода родительского интерфейса в подклассе используйте аннотацию @override, как в Java. Для реализации интерфейса применяется ключевое слово implements.

2 Реализация нескольких интерфейсов
// Абстрактный класс
abstract class Comparable {
int compareTo(Object other);
}
// Абстрактный класс
abstract class Location {
double get x;
double get y;
}
// Одновременная реализация двух интерфейсов
class Point implements Comparable, Location {
@override
double x, y;
Point(this.x, this.y);
@override
int compareTo(Object other) {
if (other is Point) {
return (x + y).compareTo(other.x + other.y);
}
return -1;
}
}
void main() {
var p = Point(2, 3);
print(p.x);
print(p.compareTo(Point(1, 1)));
}
Code language: PHP (php)
@override
double x, y;
Эта аннотация означает, что эти два свойства переопределяют абстрактные геттеры, объявленные в родительском интерфейсе Location.
Статические переменные & статические методы static
1. Статические переменные static (классовые переменные, глобально единственный экземпляр)
- Принадлежат самому классу, а не экземплярам; отложенная инициализация: создаются только при первом обращении
- Статические константы рекомендуется именовать в нижнем верблюжьем стиле (camelCase)
class Queue {
static const initialCapacity = 16; // Статическая константа
static int count = 0; // Изменяемая статическая переменная
Queue() {
count++;
}
}
void main() {
assert(Queue.initialCapacity == 16); // Инициализируется при обращении, значение по умолчанию 16
Queue(); // Увеличить count на единицу
Queue();
print(Queue.count); // 2
}
Code language: JavaScript (javascript)
2. Статические методы static (классовые методы)
- Не имеют ссылки
this, поэтому не могут обращаться к членам экземпляра; доступны только статические переменные - Вызываются напрямую по синтаксису
ИмяКласса.Метод() - Могут передаваться как константы времени компиляции в константные конструкторы
import 'dart:math';
class Point {
double x, y;
Point(this.x, this.y);
// Статический метод: вычислить прямое расстояние между двумя точками
static double distanceBetween(Point a, Point b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
}
void main() {
var a = Point(2, 2);
var b = Point(4, 4);
var dist = Point.distanceBetween(a, b);
print(dist); // ~2,828
}Code language: JavaScript (javascript)

Статические методы принадлежат классу, поэтому их нужно вызывать по имени класса, передавая два экземпляра Point как аргументы.