Проверка на нуль не вызывает продвижение типа в Dart
Я обновляю персональный пакет, основанный на фреймворке Flutter. Я заметил здесь, в исходном коде виджета Flutter Text, что есть проверка на null:
if (textSpan != null) {
properties.add(textSpan!.toDiagnosticsNode(name: 'textSpan', style: DiagnosticsTreeStyle.transition));
}
Тем не мение,
textSpan!
все еще использует
!
оператор. Не должен
textSpan
быть повышенным до типа, не допускающего значения NULL, без необходимости использования
!
оператор? Однако попытка удалить оператор дает следующую ошибку:
An expression whose value can be 'null' must be null-checked before it can be dereferenced. Try checking that the value isn't 'null' before dereferencing it.
Вот самодостаточный пример:
class MyClass {
String? _myString;
String get myString {
if (_myString == null) {
return '';
}
return _myString; // <-- error here
}
}
Я получаю ошибку времени компиляции:
Ошибка: значение типа "Строка?" не может быть возвращен из функции myString, потому что она имеет тип возвращаемого значения String.
Я думал, что выполнение нулевой проверки будет способствовать
_myString
в тип, не допускающий значения NULL. Почему нет?
Мой вопрос был решен на GitHub, поэтому я публикую ответ ниже.
4 ответа
Дарт-инженер Эрик Эрнст говорит на GitHub:
Повышение типа применимо только к локальным переменным.... Продвижение переменной экземпляра не является правильным, потому что оно может быть переопределено геттером, который выполняет вычисление и возвращает другой объект при каждом его вызове. Ср. dart-lang / language #1188 для обсуждения механизма, который похож на продвижение типов, но основан на динамических проверках, с некоторыми ссылками на связанные обсуждения.
Итак, продвижение локального типа работает:
String myMethod(String? myString) {
if (myString == null) {
return '';
}
return myString;
}
Но переменные экземпляра не продвигаются. Для этого вам нужно вручную сообщить Dart, что вы уверены, что переменная экземпляра не равна нулю в этом случае, используя
!
оператор:
class MyClass {
String? _myString;
String myMethod() {
if (_myString == null) {
return '';
}
return _myString!;
}
}
Обычно есть 3 способа решить эту проблему.
Использовать локальную переменную (рекомендуется)
Используйте локальные переменные для продвижения типа (подробнее об этой концепции здесь )
class Foo { int? i = 0; // Error double d1() => i.toDouble(); // No error double d2() { var i = this.i; // Use of local variable. if (i != null) return i.toDouble(); return -1.0; // Some default value if 'i' happens to be 'null' } }
Использовать
?.
с участием??
class Foo { int? i = 0; // Error double d1() => i.toDouble(); // No error double d2() => i?.toDouble() ?? -1.0; // Provide some default value. }
Используйте оператор Bang (!)
Вам следует использовать это решение, только если вы на 100% уверены, что переменная (
i
) не будет никогдаnull
.class Foo { int? i = 0; // Error double d1() => i.toDouble(); // No error double d2() => i!.toDouble(); // Bang operator in play. }
Используйте тернарный оператор для исправления подобных ошибок. Это пример, когда заголовок 1 может иметь значение NULL.
Theme
.of(context)
.textTheme
.headline1?.fontSize?? 32,
style: Theme.of(контекст).textTheme.headline5!.copyWith(
style: Theme.of(context).textTheme.headline5!.copyWith(
color: Colors.white
Попробуйте сделать вызов условным с помощью? или нулевую проверку безопасности -!