Для чего нужен модификатор final в Dart?

Ответ

Модификатор final используется для объявления переменной, которая может быть инициализирована только один раз. После присвоения значения его нельзя изменить. Это обеспечивает безопасность и ясность кода, явно указывая на намерение использовать переменную как константу в рамках её области видимости.

Основные случаи использования:

  1. Константы времени выполнения: Значение вычисляется при запуске программы и затем фиксируется.
  2. Защита от изменений: Гарантирует, что ссылка на объект (или значение примитива) не будет переприсвоена.
  3. Оптимизация: Помогает компилятору в некоторых оптимизациях, хотя в меньшей степени, чем const.

Примеры:

// 1. Инициализация при объявлении
final String apiBaseUrl = 'https://api.example.com';
// apiBaseUrl = '...'; // Ошибка компиляции: 'apiBaseUrl' может быть установлена только один раз.

// 2. Инициализация в конструкторе (для полей класса)
class User {
  final String id;
  final String name;

  User(this.id, this.name); // Значения присваиваются один раз при создании объекта

  // Ошибка: final поле не может быть изменено даже внутри класса
  // void changeName(String newName) { this.name = newName; }
}

void main() {
  final user = User('u1', 'Alice');
  print(user.name); // Alice
}

// 3. Значение вычисляется во время выполнения (в отличие от const)
final DateTime appStartTime = DateTime.now(); // Корректно
// const DateTime appStartTime = DateTime.now(); // Ошибка: значение не известно во время компиляции

// 4. Final с коллекциями и объектами
final List<int> numbers = [1, 2, 3];
// numbers = [4, 5]; // Ошибка: нельзя переприсвоить ссылку
numbers.add(4); // Но можно изменять содержимое mutable-объекта! Теперь список [1, 2, 3, 4]

Важное отличие от const: const создает compile-time константу, значение которой должно быть известно уже на этапе компиляции. final — это run-time константа. Все const переменные неявно являются final, но не наоборот.

// const - известно при компиляции
const double pi = 3.14159;
const List<int> constList = [1, 2, 3]; // Список также иммутабелен

// final - может быть вычислено при выполнении
final double calculatedValue = someComplexCalculation();

Ответ 18+ 🔞

Давай разжую этот final так, чтобы даже кошка соседская поняла, а то читаешь документацию — волнение ебать, как будто инструкцию к ракете изучаешь.

Смотри, чувак, final — это как татуха на жопе. Набил один раз, и всё, переделать — больно и дорого. Объявил переменную с этим словечком, присвоил ей значение, и привет. Больше ты её не тронешь. Никаких «ой, передумал». Это железобетонное намерение сказать: «Эту штуку я здесь менять не буду, отстаньте».

Где это, блядь, пригождается:

  1. Константы, которые не лень посчитать. Не в момент написания кода, а когда программа уже побежала. final — оно ленивое, подождёт.
  2. Чтобы самому себе (и другим) мозг не выносить. Поставил final на поле в классе — и все сразу видят, что это как отпечатки пальцев у объекта: дали при рождении и навсегда.
  3. Компилятору подмигнуть. Он иногда, видя final, может чуть умнее код собрать, хотя не особо на это рассчитывай.

Примеры из жизни, чтобы совсем охуенно стало понятно:

// 1. Приколол значок раз и навсегда
final String главныйСервер = 'https://api.moihlebi.ru';
// главныйСервер = 'https://govno.com'; // Ошибка, ёпта! Нельзя! Ты же уже решил!

// 2. Внутри класса — как фамилия. Родился Ивановым, Ивановым и помрёшь.
class Пользователь {
  final String id; // ID менять? Да ты что, охуел?
  final String имя; // Имя тоже фиксируем. Не Пётр сегодня, а Завтрак завтра.

  Пользователь(this.id, this.мя); // Всё прописали в паспорт при рождении (в конструкторе).

  // Вот так НЕЛЬЗЯ, иначе доверия ебать ноль будет:
  // void сменитьИмя(String новое) { this.имя = новое; } // Компилятор тебя матом пошлёт.
}

void main() {
  final юзер = Пользователь('007', 'Бонд');
  print(юзер.имя); // Бонд. И только Бонд.
}

// 3. Вот где сила! Значение можно вычислить прямо на ходу.
final DateTime моментЗапуска = DateTime.now(); // Запомнили, когда юзер тыкнул по иконке. Работает!
// const DateTime моментЗапуска = DateTime.now(); // А вот так — НЕТ! const требует знать всё заранее, а сейчас-то какое время? Хуй его знает.

// 4. ВАЖНОЕ ПРЕДУПРЕЖДЕНИЕ, ЧТОБЫ ТЫ НЕ ОХУЕЛ ПОТОМ!
final List<int> цифры = [1, 2, 3];
// цифры = [4, 5, 6]; // ОШИБКА! Ссылку-то менять нельзя!
цифры.add(4); // А ВОТ ЭТО — МОЖНО! ОБЪЕКТ-ТО ВНУТРИ ИЗМЕНЯЕМЫЙ! Теперь список [1, 2, 3, 4].
// final защищает КОРОБКУ (переменную), а не то, что ВНУТРИ КОРОБКИ (объект). Запомни, ядрёна вошь!

Чем final не const, или история про близнецов-братьев.

const — это отличник, который всё знает ещё до урока. Значение должно быть ясно прямо при компиляции. final — это практик, который может результат лабораторной работы только после эксперимента выдать.

// const — знает всё заранее, как будто на дворе 2002-й год и расписание на стене.
const double pi = 3.14159;
const List<int> идеальныйСписок = [1, 2, 3]; // И этот список тоже заморожен намертво, в него нихуя не добавишь.

// final — может подождать, пока программа запустится и посчитает.
final double результатХитровыебаннойФормулы = высчитатьЧтоТоСложное();

Короче, final — твой верный способ сказать: «Эту переменную я здесь больше не трону». Используй, чтобы код не превращался в пиздопроебибну, где всё плывёт и меняется непонятно где.