Ответ
Иммутабельный (неизменяемый) класс — это класс, экземпляры которого не могут изменять своё состояние после создания. Все его поля объявлены как final, и он не предоставляет никаких методов, которые могли бы изменить значения этих полей.
Принципы создания иммутабельного класса в Dart:
- Все поля —
final: Это гарантирует, что поле может быть присвоено только один раз — в конструкторе. - Конструктор может быть
const: Если все поля final и конструктор помечен какconst, это позволяет компилятору создавать canonicalized (канонические) экземпляры, что экономит память и улучшает производительность. - Отсутствие сеттеров и мутирующих методов: Нет методов типа
setName(). - Защита от изменяемых полей: Если класс содержит коллекции или другие объекты, необходимо обеспечить, чтобы и они были иммутабельны, или возвращать их защищённые копии.
Пример иммутабельного класса в Dart:
import 'package:collection/collection.dart'; // Для глубокого сравнения и копирования
class ImmutablePerson {
final String name;
final int age;
final List<String> hobbies; // Ссылка на изменяемый список!
// Конструктор с const возможен, если все поля final и примитивны/иммутабельны.
// Список hobbies делает const конструктор невозможным в данном виде.
ImmutablePerson(this.name, this.age, [List<String>? hobbies])
: hobbies = List.unmodifiable(hobbies ?? []); // Защитная обёртка
// Метод для "изменения" состояния, возвращающий новый экземпляр
ImmutablePerson copyWith({String? name, int? age, List<String>? hobbies}) {
return ImmutablePerson(
name ?? this.name,
age ?? this.age,
hobbies ?? this.hobbies,
);
}
@override
String toString() => 'Person(name: $name, age: $age, hobbies: $hobbies)';
}
void main() {
// Создание экземпляра
var person1 = ImmutablePerson('Alice', 30, ['reading', 'hiking']);
print(person1); // Person(name: Alice, age: 30, hobbies: [reading, hiking])
// Попытка изменить список hobbies извне не сработает
// person1.hobbies.add('gaming'); // Throws: Unsupported operation: Cannot add to an unmodifiable list
// "Изменение" через создание новой копии (паттерн copy-with)
var person2 = person1.copyWith(age: 31);
print(person2); // Person(name: Alice, age: 31, hobbies: [reading, hiking])
print(identical(person1, person2)); // false (это разные объекты)
}
Преимущества иммутабельности в контексте Flutter/Dart:
- Потокобезопасность: Иммутабельные объекты можно свободно передавать между изолятами (isolates) без синхронизации.
- Предсказуемость: Состояние объекта не может неожиданно измениться, что упрощает отладку и рассуждение о коде.
- Кэширование и оптимизация:
const-конструкторы позволяют компилятору кэшировать и повторно использовать экземпляры. - Ключ для состояний: Идеально подходят для использования в качестве ключей в
Mapили значений вSet, так как ихhashCodeне меняется. - Состояние в управлении состоянием: Библиотеки вроде
flutter_blocилиriverpodчасто полагаются на иммутабельные классы для представления состояний, что облегчает их сравнение (==) и триггерит перерисовку виджетов только при реальном изменении данных.