Хочу рассказать про операторы проверки на null в Dart, а точнее про не очевидные операторы проверки на null, которые открывают интересные возможности или просто упрощают написание кода. Ну или если вы видели два вопроса в коде ?? и не понимаете что это означает, то вам сюда.
Проверка на null
Какой самый простой вариант проверки на null в Dart? Правильно! Просто проверить его на равенство с null в блоке if:
А как на счет более частого варианты использования? Чаще мы стараемся уйти от использования переменных которые могут быть null. Хочется сделать так - если переменная не null то используем её, а если null то значение по умолчанию. Тут у нас уже получается достаточно много кода... А если таких переменных 10? Что делать?
Тут нам приходит на помощь:
If-null оператор: ??
Это как логическое или ||, только проверяет оно на null:
Данная конструкция вернет index если он не null или значение if_index_null.
От псевдокода к практике. Давайте напишем проверку, если при расчете индекса получился null, то присваивать дефолтное значение -1
Где это требуется часто на практике? При реализации функции copyWith - если в переменной передали null, то копируем старое значение. При парсинге данных json - если какой-то параметр не пришел, то ставим дефолтный. И так далее.
Кстати можно было бы написать и так:
Тоже вполне себе рабочая проверка. Для упрощения этой записи приходит на помощь:
Null-aware оператор присваивания: ??=
Присвоить новое значение переменной если она сейчас равна null
На практике это бывает используется при реализации кеширования, например, кеширование вычисляемого поля:
Это достаточно сложный для восприятия оператор, поэтому используется нечасто.
Зато чаще приходится добираться до вложенных значений или функций объекта, который может быть null. И, особенно если вложенность большая, писать if проверки довольно муторно и очень снижает читаемость кода. К счастью нам может помочь:
Null-aware оператор доступа: ?.
Взять значение из объекта если объект не null, ну а если объект null то вернем null.
Например, нам нужно достать округленную цену из продукта, а он при этом может быть null
Если у нас есть product и если у него есть цена, то округляем до целого, если нет - то возвращаем null.
Кстати, такой подход довольно удобен с функциями - можно делать вызов вложенных функций если объект не null (а если он null - то ничего делаться не будет):
Функции могут быть также переданы или храниться как параметры, которым может быть присвоен null. Когда нам нужно её вызвать если она не null используем метод call у любой функции:
На практике это используется, например, для вызова калбека если он передан.
С функциями понятно, а как на счет листов/массивов? Что если нам нужно обратиться к первому элементу массива который сам может быть null? Нам поможет:
Null-aware оператор индекса: ?[]
Он берет значение в массиве по индексу, если массив не null, ну а если массив null то вернем null.
Возьмём у нашего злополучного продукта первый доступный размер:
На практике такое требуется очень не часто, да и всё равно надо быть уверенным, что массив не пустой, даже если он не null. А вот что требуется чаще, так это копирование элементов массива если он не null, в этом нам поможет:
Null-aware spread оператор: ...?
С помощью него можно вставить значения массива в другой массив. А если у нас исходный массив равен null, то ничего не вставлять.
К примеру, мы придумали, что все продукты будут также иметь отрицательную размерную сетку (чтобы это не значило). И хотим получить весь размерный ряд (помним что как продукт, так и его массив размеров может быть null):
Такое много где требуется, но самое интересно использование - это вставка переданного или сгенерированного массива виджетов в колонку между другими виджетами.
Ну и в качестве заключения, последний в нашем списке:
Null-aware каскадный оператор: ?..
Если объект не null, то вызываем у него какой-то метод каскадно. У второго каскадного оператора при этом уже вопрос ставить не надо:
Используйте null-aware операторы - так код становится меньше, а следовательно проще и понятнее (хотя не факт конечно, так что не борьщите).
Если вы вдруг позабыли, что это вообще за null такой и что такое null-safety всегда можно посмотреть в официальной документации: