Операторы ? и !
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 25 из 110.
При работе с типами, допускающими значение null, Dart предоставляет два основных оператора для безопасного доступа к значениям. Оператор обращения, учитывающий null ?. и оператор утверждения null ! дают вам контроль над тем, как вы обрабатываете потенциально пустые значения.
Оператор ?. безопасно обращается к свойствам или методам nullable-значения. Если значение равно null, все выражение возвращает null вместо того, чтобы вызвать ошибку:
String? name = 'Alice';
print(name?.length); // 5
name = null;
print(name?.length); // null (без ошибок!)Оператор ! сообщает Dart: «Я уверен, что это значение не равно null». Он преобразует тип, допускающий значение null, в тип, не допускающий его, но выдает ошибку, если значение на самом деле равно null:
String? maybeName = 'Bob';
String definite = maybeName!; // OK - мы утверждаем, что значение не равно null
print(definite.length); // 3
String? empty = null;
// String crash = empty!; // Ошибка времени выполнения! Значение было nullИспользуйте ?., когда вы хотите корректно обрабатывать значения null. Используйте ! только тогда, когда вы абсолютно уверены, что значение существует — возможно, после проверки его с помощью оператора if или когда вы знаете, что логика гарантирует наличие значения. Чрезмерное использование ! сводит на нет цель безопасности null и может привести к сбоям во время выполнения.
Задание
ЛегкоДавайте создадим систему инвентаризации товаров, которая демонстрирует, как безопасно работать с nullable-значениями, используя операторы ?. и !.
Вы создадите два файла для организации вашего кода:
product.dart: Определите классProduct, который представляет товар в инвентаре. Некоторые товары имеют полную информацию, в то время как другие все еще находятся в процессе каталогизации. Ваш класс должен иметь:- Non-nullable
String name— у каждого товара должно быть название - Non-nullable
double price— у каждого товара должна быть цена - Nullable
String? description— у некоторых товаров пока нет описания - Nullable
String? category— некоторые товары еще не распределены по категориям - Конструктор, который принимает
nameиpriceкак обязательные параметры - Метод
getDescriptionLength(), который возвращает длину описания, используя?., илиnull, если описание отсутствует - Метод
getCategoryUppercase(), который возвращает категорию в верхнем регистре, используя!— этот метод предполагает, что категория была установлена - Метод
displayProduct(), который выводит информацию о товаре, показывая"No description", когда описание равно null, и"Uncategorized", когда категория равна null
- Non-nullable
main.dart: Импортируйте ваш класс продукта и продемонстрируйте работу обоих операторов:- Создайте товар с названием
'Laptop'и ценой999.99, затем установите его описание как'High-performance computing device'и категорию как'Electronics' - Создайте товар с названием
'Mystery Item'и ценой49.99(оставьте описание и категорию как null) - Для Laptop: вызовите
displayProduct(), выведите'Description length: [length]', используяgetDescriptionLength(), и выведите'Category: [uppercase]', используяgetCategoryUppercase() - Для Mystery Item: вызовите
displayProduct()и выведите'Description length: [length]', используяgetDescriptionLength()(это выведетnull)
- Создайте товар с названием
Метод displayProduct() должен выводить данные в следующем формате:
Product: [name] - $[price]
Description: [description or "No description"]
Category: [category or "Uncategorized"]Ожидаемый вывод:
Product: Laptop - $999.99
Description: High-performance computing device
Category: Electronics
Description length: 32
Category: ELECTRONICS
Product: Mystery Item - $49.99
Description: No description
Category: Uncategorized
Description length: nullШпаргалка
Оператор обращения с проверкой на null ?. позволяет безопасно обращаться к свойствам или методам nullable-значений. Если значение равно null, он возвращает null вместо того, чтобы вызывать ошибку:
String? name = 'Alice';
print(name?.length); // 5
name = null;
print(name?.length); // null (без сбоя!)Оператор утверждения о ненулевом значении ! преобразует nullable-тип в non-nullable. Используйте его только тогда, когда вы уверены, что значение не равно null, так как он вызовет ошибку времени выполнения, если значение на самом деле окажется null:
String? maybeName = 'Bob';
String definite = maybeName!; // OK — утверждает, что значение не null
print(definite.length); // 3
String? empty = null;
// String crash = empty!; // Ошибка времени выполнения!Используйте ?. для корректной обработки null и ! только тогда, когда вы абсолютно уверены в существовании значения.
Попробуйте сами
import 'product.dart';
void main() {
// TODO: Создать продукт с названием 'Laptop' и ценой 999.99
// Затем установить описание 'High-performance computing device'
// И установить категорию 'Electronics'
// TODO: Создать продукт с названием 'Mystery Item' и ценой 49.99
// Оставить описание и категорию как null
// TODO: Для Laptop:
// - Вызвать displayProduct()
// - Вывести "Description length: [length]", используя getDescriptionLength()
// - Вывести "Category: [uppercase]", используя getCategoryUppercase()
// TODO: Для Mystery Item:
// - Вызвать displayProduct()
// - Вывести "Description length: [length]", используя getDescriptionLength()
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий