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

В чём разница между final и const в Dart?

1.0 Junior🔥 171 комментариев
#Dart

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

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

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

Разница между final и const в Dart

final и const — это два механизма для создания неизменяемых переменных в Dart, но они работают по-разному и используются в разных ситуациях.

final (Неизменяемая переменная)

final — это переменная, которая может быть установлена только один раз, но значение определяется во время выполнения (runtime).

// Базовый пример
final String name = 'Alice';
// name = 'Bob'; // ОШИБКА - нельзя переназначить

// Тип можно опустить
final age = 25; // age будет типа int

// Может быть инициализирована позже
final String? email;
email = 'alice@example.com'; // OK - первое присвоение
// email = 'bob@example.com'; // ОШИБКА - уже присвоено

// Может быть вычисляемым значением
final timestamp = DateTime.now();
final random = Random().nextInt(100);
final sum = 5 + 3; // Вычисляется в runtime

// Для списков и объектов
final List<int> numbers = [1, 2, 3];
numbers.add(4); // OK - элемент добавляется, сама ссылка не меняется

final Map<String, String> user = {'name': 'Alice'};
user['age'] = '25'; // OK - значение меняется
// user = {}; // ОШИБКА - ссылка не может измениться

Характеристики final:

  • Runtime инициализация — значение определяется при выполнении
  • Один раз — может быть установлена только один раз
  • Для переменных — используется для переменных и полей класса
  • Контейнеры мутабельны — сам контейнер неизменяемый, но его содержимое может меняться
  • Ленивая инициализация — можно задержать инициализацию
// Использование в классе
class User {
  final String id;
  final String name;
  
  User(this.id, this.name);
}

final user = User('123', 'Alice');
// user = User('456', 'Bob'); // ОШИБКА
user.name; // OK - можно читать

const (Константа времени компиляции)

const — это переменная, значение которой известно на этапе компиляции и никогда не меняется.

// Базовый пример
const String name = 'Alice';
// name = 'Bob'; // ОШИБКА

// Значение должно быть известно на этапе компиляции
const int age = 25; // OK
const double pi = 3.14159; // OK
// const int random = Random().nextInt(100); // ОШИБКА - runtime значение
// const String date = DateTime.now().toString(); // ОШИБКА - runtime

// Для коллекций - все элементы тоже должны быть const
const List<int> numbers = [1, 2, 3]; // OK
const Map<String, int> ages = {'Alice': 25, 'Bob': 30}; // OK

// const List<int> mixed = [1, DateTime.now()]; // ОШИБКА - DateTime runtime

// На верхнем уровне (top-level)
const String appName = 'MyApp'; // OK
const String appVersion = '1.0.0'; // OK

// Может быть с const конструктором
const Point point = Point(10, 20); // Если Point имеет const конструктор

Требования для const:

  • Compile-time значение — известно при компиляции
  • Const конструктор — для объектов нужен const конструктор класса
  • Иммутабельные значения — все элементы иммутабельны
  • Статические значения — не может зависеть от runtime данных
// Const конструктор
class Point {
  final int x;
  final int y;
  
  const Point(this.x, this.y); // const конструктор
}

const point1 = Point(10, 20); // Может быть const
const point2 = Point(10, 20); // Это тот же объект в памяти!
print(identical(point1, point2)); // true - один объект

final point3 = Point(10, 20); // Другой объект
print(identical(point1, point3)); // false

Сравнение

Аспектfinalconst
ИнициализацияRuntimeCompile-time
Может быть вычисленоДаНет
Ленивая инициализацияДаНет
ПроизводительностьНормальнаяЛучше (оптимизация)
Может использоваться как constНетДа
Требует const конструкторНетДа (для объектов)
Контейнер мутабеленДаНет
Использование памятиКаждый раз новыйПереиспользует объект

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

const — для известных значений:

// Конфигурация приложения
const String apiBase = 'https://api.example.com';
const int maxRetries = 3;
const Duration timeout = Duration(seconds: 30);

// Константы виджетов
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: const HomePage(), // const предпочтительнее
    );
  }
}

class Button extends StatelessWidget {
  const Button({Key? key}) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {},
      child: const Text('Click me'), // const для оптимизации
    );
  }
}

final — для значений в runtime:

// Переменные, которые вычисляются
final now = DateTime.now();
final userPrefs = await loadPreferences();
final api = ApiClient(baseUrl: configUrl);

// Параметры функций
class UserService {
  final DatabaseClient db;
  final Logger logger;
  
  UserService(this.db, this.logger);
  
  Future<User> getUser(String id) async {
    final user = await db.fetchUser(id);
    logger.info('User fetched');
    return user;
  }
}

// Локальные переменные
void processData(List<int> items) {
  final sum = items.fold(0, (a, b) => a + b);
  final average = sum / items.length;
  final max = items.reduce((a, b) => a > b ? a : b);
}

Переиспользование const для оптимизации:

// Плохо - создаёт новый объект каждый раз
Widget buildButton() {
  return ElevatedButton(
    onPressed: () {},
    child: Text('Submit'),
  );
}

// Хорошо - переиспользует const объект
const _submitButton = ElevatedButton(
  onPressed: null, // const нельзя использовать с callback
  child: Text('Submit'),
);

Widget buildButton() => _submitButton;

Const конструктор

// Класс с const конструктором
class Color {
  final int red;
  final int green;
  final int blue;
  
  // const конструктор - все поля должны быть final
  const Color(this.red, this.green, this.blue);
}

const primaryColor = Color(33, 150, 243); // OK
const secondaryColor = Color(76, 175, 80); // OK

// Интересный факт
const c1 = Color(255, 0, 0);
const c2 = Color(255, 0, 0);
print(identical(c1, c2)); // true! Один объект в памяти

final c3 = Color(255, 0, 0);
print(identical(c1, c3)); // false

Правила использования

Используй const когда:

  • Значение известно на этапе компиляции
  • Нужна оптимизация памяти
  • Создаёшь immutable виджеты
  • Определяешь константы приложения

Используй final когда:

  • Значение определяется в runtime
  • Нужна инициализация из параметров
  • Загружаешь данные из API или БД
  • Используешь результаты вычислений

Золотое правило: Если сомневаешься, используй final. const — это оптимизация для известных значений.

В чём разница между final и const в Dart? | PrepBro