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

Что такое Immutable объекты?

2.0 Middle🔥 201 комментариев
#Dart#ООП и паттерны

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

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

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

Что такое Immutable объекты

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

Основная идея

// ❌ Mutable объект (может быть изменен)
class MutableUser {
  String name;
  int age;
  
  MutableUser(this.name, this.age);
}

var user = MutableUser("Alice", 30);
user.age = 31; // Можно изменить! Неопасно

// ✅ Immutable объект (не может быть изменен)
class ImmutableUser {
  final String name;
  final int age;
  
  const ImmutableUser(this.name, this.age);
}

var user = ImmutableUser("Alice", 30);
user.age = 31; // ❌ Ошибка компиляции!

Зачем нужны immutable объекты

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

  1. Thread safety — объект безопасен в многопоточной среде
  2. Predictability — код поведение предсказуемо
  3. Caching — объекты можно безопасно кешировать
  4. Performance — оптимизация компилятором
  5. Debugging — легче отследить изменения

Immutable класс в Dart

// Классический способ
class User {
  final String name;
  final int age;
  final String email;
  
  const User(this.name, this.age, this.email);
  
  // Для изменения данных создаем новый объект (copyWith)
  User copyWith({
    String? name,
    int? age,
    String? email,
  }) {
    return User(
      name ?? this.name,
      age ?? this.age,
      email ?? this.email,
    );
  }
  
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is User &&
          runtimeType == other.runtimeType &&
          name == other.name &&
          age == other.age &&
          email == other.email;
  
  @override
  int get hashCode => name.hashCode ^ age.hashCode ^ email.hashCode;
}

void main() {
  var user = User("Alice", 30, "alice@example.com");
  
  // Вместо user.age = 31;
  // Создаем новый объект
  var updatedUser = user.copyWith(age: 31);
  
  print(user.age);        // 30 (исходный не изменился)
  print(updatedUser.age); // 31 (новый объект)
}

Использование @immutable аннотации

import 'package:flutter/foundation.dart';

@immutable
class Product {
  final String id;
  final String name;
  final double price;
  final List<String> tags; // ⚠️ Осторожно!
  
  const Product({
    required this.id,
    required this.name,
    required this.price,
    required this.tags,
  });
}

void main() {
  var product = Product(
    id: "1",
    name: "Laptop",
    price: 999.99,
    tags: ["electronics", "computers"],
  );
  
  // ⚠️ Опасно! Список можно изменить
  product.tags.add("new");
  
  // Правильно: use immutable lists
  var product2 = Product(
    id: "2",
    name: "Phone",
    price: 599.99,
    tags: const ["electronics", "mobile"],
  );
}

Immutable коллекции

// Использовать const для immutable коллекций
const List<String> myList = ["a", "b", "c"];
const Map<String, int> myMap = {"a": 1, "b": 2};
const Set<int> mySet = {1, 2, 3};

// Или использовать UnmodifiableListView
import 'dart:collection';

class Config {
  final List<String> _tags;
  
  Config(List<String> tags) : _tags = UnmodifiableListView(tags);
  
  List<String> get tags => _tags; // Невозможно изменить
}

void main() {
  var config = Config(["tag1", "tag2"]);
  config.tags.add("tag3"); // ❌ UnsupportedError
}

Immutable в Flutter State Management

// Пример с Provider
import 'package:flutter/foundation.dart';

@immutable
class AppState {
  final String title;
  final int counter;
  final bool isLoading;
  
  const AppState({
    required this.title,
    required this.counter,
    required this.isLoading,
  });
  
  // Копирование с изменениями
  AppState copyWith({
    String? title,
    int? counter,
    bool? isLoading,
  }) {
    return AppState(
      title: title ?? this.title,
      counter: counter ?? this.counter,
      isLoading: isLoading ?? this.isLoading,
    );
  }
}

class AppNotifier extends ChangeNotifier {
  AppState _state = AppState(
    title: "MyApp",
    counter: 0,
    isLoading: false,
  );
  
  AppState get state => _state;
  
  void increment() {
    // Создаем новый immutable объект
    _state = _state.copyWith(counter: _state.counter + 1);
    notifyListeners();
  }
  
  void setLoading(bool loading) {
    _state = _state.copyWith(isLoading: loading);
    notifyListeners();
  }
}

Практический пример для Flutter

@immutable
class UserProfile {
  final String id;
  final String username;
  final String email;
  final DateTime createdAt;
  final List<String> interests;
  
  const UserProfile({
    required this.id,
    required this.username,
    required this.email,
    required this.createdAt,
    required this.interests,
  });
  
  // Обновление профиля
  UserProfile updateEmail(String newEmail) {
    return UserProfile(
      id: id,
      username: username,
      email: newEmail,
      createdAt: createdAt,
      interests: interests,
    );
  }
  
  // Или удобнее
  UserProfile copyWith({
    String? id,
    String? username,
    String? email,
    DateTime? createdAt,
    List<String>? interests,
  }) {
    return UserProfile(
      id: id ?? this.id,
      username: username ?? this.username,
      email: email ?? this.email,
      createdAt: createdAt ?? this.createdAt,
      interests: interests ?? this.interests,
    );
  }
  
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is UserProfile &&
          runtimeType == other.runtimeType &&
          id == other.id &&
          username == other.username &&
          email == other.email &&
          createdAt == other.createdAt &&
          interests == other.interests;
  
  @override
  int get hashCode =>
      id.hashCode ^
      username.hashCode ^
      email.hashCode ^
      createdAt.hashCode ^
      interests.hashCode;
}

Пакеты для Immutable объектов

freezed — автоматическое генерирование immutable классов:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';

@freezed
class User with _$User {
  const factory User({
    required String id,
    required String name,
    required int age,
  }) = _User;
}

void main() {
  var user = User(id: "1", name: "Alice", age: 30);
  
  // copyWith генерируется автоматически
  var updated = user.copyWith(age: 31);
  
  // == и hashCode тоже генерируются
  print(user == updated); // false
}

Лучшие практики

Делай:

  • ✅ Используй const для конструкторов immutable объектов
  • ✅ Используй final для всех полей
  • ✅ Реализуй copyWith метод
  • ✅ Переопредели == и hashCode
  • ✅ Аннотируй классы с @immutable
  • ✅ Используй пакеты вроде freezed для автоматизации

Избегай:

  • ❌ Mutable коллекции внутри immutable объектов
  • ❌ Прямое изменение полей
  • ❌ Забывчивость переопределить == и hashCode

Заключение

Immutable объекты — это основа надежного Dart и Flutter кода. Они:

  • Делают код безопаснее
  • Облегчают state management
  • Улучшают производительность
  • Делают debugging проще

Для интервью на Flutter Developer важно понимать зачем и как использовать immutable объекты.

Что такое Immutable объекты? | PrepBro