/// /// Flutter Mobile Developer /// "Conditional member access" or "null safe" operator /// Still actual but keep in the mind /// https://nullsafety.dartpad.dev/ ///
class Address { String postalCode; }
class Employee { Address address; }
class EmployeeFactory { static Employee createEmployee() => null; }
void main() { final employee = EmployeeFactory.createEmployee();
// Мы не уверены, будет ли инстанс класса Employee создан // поэтому для чтения поля postalCode // мы должны выполнить следующие проверки // Без проверки получим Null Pointer Exception в runtime // Обычный сценарий - это проверка на null всей вложенной структуры // Сначала проверяем, что сам инстанс not null, затем address not null // и так далее - код становится трудно читать if (employee != null && employee.address != null) { print(employee.address.postalCode); }
// но мы можем использовать более удобный способ // для безопасного доступа к полю, используя conditional member access // используя синтаксис ?. мы возвращаем null, если при обращении к какому то полю // по всей цепочке вниз это поле оказалось null print(employee?.address?.postalCode); // should be null
// еще один интересный оператор ?? - возвращает нам левую часть выражения, // если она != null, в противном случае возвращает прравую print(employee?.address?.postalCode ?? 'empty'); // should be empty string
// в dart есть еще один интересный assignment оператор ??=, // который можно использовать для инициализации переменной // только в том случае, если она еще == null // в примере дальше переменная firstValueInLoop получит свое значение // только при первой попытке присвоить новое значение int firstValueInLoop; for (var index = 10; index > 0; index--) { firstValueInLoop ??= index; } print('firstValueInLoop is $firstValueInLoop'); // should be 10
// этот же оператор можно использовать для
// ленивой инициализации полей класса (keep in the mind "late" keyword)
// например, как для свойство fibonacci в классе
// ExpensiveInitialization
final expensive = ExpensiveInitialization(5);
print('first call >
class ExpensiveInitialization { int _fibonacci;
final int _number;
int _calculateFibonacci(int n) { print('expensive operation'); return n <= 2 ? 1 : _calculateFibonacci(n - 2) + _calculateFibonacci(n - 1); }
ExpensiveInitialization(this._number) { print('ExpensiveInitialization intance ctor'); }
/// здесь приватное поле _fibonacci получает свое новое значение при первом обращении, /// и потом это же значение (на самом деле значение операции assignment) возвращается геттером свойства fibonacci int get fibonacci => _fibonacci ??= _calculateFibonacci(_number); }