← Назад к вопросам

Как сделать класс имутабельным?

2.0 Middle🔥 131 комментариев
#Dart

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как сделать класс имутабельным

Имутабельность — это свойство объекта, при котором его состояние не может быть изменено после создания. Это критически важно для безопасности в многопоточной среде и предсказуемости кода.

Основные способы создания имутабельных классов

1. Использование конструктора const и финального поля

class User {
  final String name;
  final String email;
  final int age;
  
  const User({
    required this.name,
    required this.email,
    required this.age,
  });
}

// Использование
const user = User(
  name: John,
  email: john@example.com,
  age: 30,
);

// ✅ Работает благодаря const
const user2 = User(
  name: John,
  email: john@example.com,
  age: 30,
);
print(user == user2); // true, один и тот же объект в памяти

2. Аннотация @immutable

import package:flutter/foundation.dart;

@immutable
class Product {
  final String id;
  final String title;
  final double price;
  final List<String> categories;
  
  const Product({
    required this.id,
    required this.title,
    required this.price,
    required this.categories,
  });
}

Важно: @immutable — это только аннотация для анализатора, не гарантирует неизменяемость рантайме.

3. Dataclass через freezed (рекомендуется)

import package:freezed_annotation/freezed_annotation.dart;

part user.freezed.dart;
part user.g.dart;

@freezed
class User with _$User {
  const factory User({
    required String id,
    required String name,
    required String email,
  }) = _User;
  
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

Преимущества freezed:

  • Автоматическая генерация copyWith
  • Equals и hashCode
  • toString()
  • Serialization/Deserialization
  • Pattern matching в Dart 3.0+
final user = User(
  id: 1,
  name: John,
  email: john@example.com,
);

// copyWith создаёт новый объект
final updatedUser = user.copyWith(name: Jane);

print(user == updatedUser); // false, разные объекты

4. Защита от изменения мутабельных коллекций

// ❌ Проблема: List внутри может быть изменён
@immutable
class Team {
  final List<String> members;
  
  const Team({required this.members});
}

final team = Team(members: [Alice, Bob]);
team.members.add(Charlie); // ❌ Список изменён!

// ✅ Решение 1: использовать UnmodifiableListView
import dart:collection;

@immutable
class Team {
  final List<String> _members;
  
  List<String> get members => UnmodifiableListView(_members);
  
  const Team({required List<String> members}) : _members = members;
}

final team = Team(members: [Alice, Bob]);
team.members.add(Charlie); // Ошибка: UnsupportedError

// ✅ Решение 2: с freezed
@freezed
class Team with _$Team {
  const factory Team({
    required List<String> members,
  }) = _Team;
}

5. Правила создания имутабельного класса

@immutable
class Point {
  // ✅ Все поля final
  final double x;
  final double y;
  
  // ✅ Конструктор const
  const Point(this.x, this.y);
  
  // ✅ Методы не изменяют состояние, создают новые объекты
  Point translate(double dx, double dy) {
    return Point(x + dx, y + dy);
  }
  
  // ✅ Переопределён equals и hashCode
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Point &&
          runtimeType == other.runtimeType &&
          x == other.x &&
          y == other.y;
  
  @override
  int get hashCode => x.hashCode ^ y.hashCode;
}

Практические примеры в Flutter

State Management (BLoC/Riverpod)

@freezed
class CounterState with _$CounterState {
  const factory CounterState({
    @Default(0) int count,
  }) = _CounterState;
}

// При изменении состояния создаётся новый объект
final newState = state.copyWith(count: state.count + 1);

Сравнение объектов без equals

const user1 = User(
  id: 1,
  name: John,
  email: john@example.com,
);

const user2 = User(
  id: 1,
  name: John,
  email: john@example.com,
);

print(identical(user1, user2)); // true (const == кеш)
print(user1 == user2); // true

Преимущества имутабельности

Безопасность в многопоточности — нет race conditions ✅ Предсказуемость — состояние не может неожиданно измениться ✅ Легче отлаживать — проще отследить изменения ✅ Оптимизация — const объекты кешируются ✅ Функциональное программирование — совместимость с реактивным стилем

Заключение

Для современного Flutter-разработчика freezed — лучший выбор. Он автоматизирует всю работу по созданию имутабельных классов и избавляет от boilerplate кода.

Как сделать класс имутабельным? | PrepBro