Ключевое слово Late и Null-безопасность
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy. Урок 26 из 110.
Иногда вам нужна переменная, допускающая значение null, но вы не можете сразу присвоить ей значение. Ключевое слово late сообщает Dart: «Я обещаю инициализировать её до того, как использовать».
late String description;
void setup() {
description = 'Initialized later';
}
void main() {
setup();
print(description); // Работает - инициализировано перед использованием
}Без late Dart пожаловался бы, что description не initialized. С late ты берёшь на себя ответственность за то, чтобы до обращения к нему у него было значение. Если попытаться прочитать его до инициализации, возникнет ошибка во время выполнения.
Распространённый вариант использования — ресурсоёмкие вычисления, которые вы хотите отложить до момента, когда они действительно понадобятся:
class DataProcessor {
late List<int> data = _loadData();
List<int> _loadData() {
print('Loading data...');
return [1, 2, 3, 4, 5];
}
}
DataProcessor processor = DataProcessor();
// 'Loading data...' ещё не напечаталось
print(processor.data); // Теперь оно загружается и печатаетсяМетод _loadData() выполняется только при первом обращении к data, а не при создании объекта. Такая ленивая инициализация может повысить производительность, если значение никогда не понадобится.
Используйте late, когда вы уверены, что переменная будет инициализирована до использования, но не можете сделать это при объявлении. Это обещание компилятору — нарушьте его, и ваша программа завершится с ошибкой.
Задание
ЛегкоДавайте создадим менеджер конфигурации, демонстрирующий возможности переменных late для отложенной инициализации. Вы создадите систему, в которой ресурсоёмкие операции настройки выполняются только тогда, когда конфигурация действительно необходима.
Вы организуете код в двух файлах:
config_manager.dart: объявите классConfigManager, который управляет настройками приложения. Класс должен иметь:String appName, передаваемый через constructorlate String databaseUrl, который будет инициализирован позже с помощью метода настройкиlate int maxConnectionsс ленивой инициализацией, которая при первом обращении выводитCalculating max connections...и возвращает100- метод
setupDatabase(String url), который инициализируетdatabaseUrl - метод
displayConfig(), который выводит текущую конфигурацию
main.dart: импортируйте свой менеджер конфигурации и продемонстрируйте работу переменных late:- Создайте
ConfigManagerс именем приложения'MyApp' - Выведите
Config created - Вызовите
setupDatabaseс URL'localhost:5432/mydb' - Выведите
Accessing max connections... - Выведите значение
maxConnections - Выведите
Accessing max connections again... - Снова выведите значение
maxConnections(обратите внимание, что сообщение о вычислении не повторяется) - Вызовите
displayConfig()
- Создайте
Метод displayConfig() должен выводить данные в следующем формате:
--- Configuration ---
App: [appName]
Database: [databaseUrl]
Max Connections: [maxConnections]Обратите внимание, что вычисление maxConnections с ленивой инициализацией выполняется только один раз — при первом обращении к нему. В этом и заключается преимущество late с инициализатором!
Ожидаемый результат:
Config created
Accessing max connections...
Calculating max connections...
100
Accessing max connections again...
100
--- Configuration ---
App: MyApp
Database: localhost:5432/mydb
Max Connections: 100Попробуйте сами
import 'config_manager.dart';
void main() {
// TODO: Создайте ConfigManager с именем приложения 'MyApp'
// TODO: Print 'Config created'
// TODO: Call setupDatabase with URL 'localhost:5432/mydb'
// TODO: Print 'Accessing max connections...'
// TODO: Print the value of maxConnections
// TODO: Print 'Accessing max connections again...'
// TODO: Print the value of maxConnections again
// (обратите внимание, что сообщение о вычислении не повторяется)
// TODO: Вызовите displayConfig()
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн РепозиторийПотренируйтесь самостоятельно: Онлайн-компилятор Dart