#dart
#flutter #dart #dart-нулевая безопасность
Вопрос:
Я слышал о новой функции Dart null safety language (NNBD), в настоящее время являющейся «ненулевым» экспериментом«. Предполагается, что по умолчанию вводится значение, не равное нулю.
Спецификацию функции можно найти здесь, а языковую проблему GitHub — здесь .
Как это работает и где я могу это попробовать?
Комментарии:
1. Команда IMHO Dart закончила это делать, точно так же, как Java заставляет нас перехватывать все исключения. Код стал очень шумным.
2. Первая ссылка разорвана (404).
Ответ №1:
1. Нулевая безопасность / ненулевая (по умолчанию)
Функция null safety / non-nullable (по умолчанию), сокращенно NNBD, в настоящее время можно найти на nullsafety.dartpad.dev .
Имейте в виду, что вы можете прочитать полную спецификацию здесь и полную дорожную карту здесь . Теперь для Dart также официально объявлена звуковая нулевая безопасность.
2.1. Что означает ненулевое значение по умолчанию?
void main() {
String word;
print(word); // illegal
word = 'Hello, ';
print(word); // legal
}
Как вы можете видеть выше, переменная, не указывающая значение null по умолчанию, означает, что каждая переменная, которая обычно объявляется, не может быть null
. Следовательно, любая операция, обращающаяся к переменной до ее назначения, является незаконной.
Кроме того, присвоение null
переменной, не имеющей значения null, также не допускается:
void main() {
String word;
word = null; // forbidden
world = 'World!'; // allowed
}
2.1.1. Как это мне помогает?
Если переменная не обнуляема, вы можете быть уверены, что это никогда null
не будет . Из-за этого вам никогда не нужно проверять это заранее.
int number = 4;
void main() {
if (number == null) return; // redundant
int sum = number 2; // allowed because number is also non-nullable
}
2.1.2. Запомнить
Поля экземпляра в классах должны быть инициализированы, если они не обнуляются:
class Foo {
String word; // forbidden
String sentence = 'Hello, World!'; // allowed
}
Смотрите late
ниже, чтобы изменить это поведение.
2.2. Типы с нулевым значением ( ?
)
Вы можете использовать типы с нулевым значением, добавляя знак вопроса ?
к типу переменной:
class Foo {
String word; // forbidden
String? sentence; // allowed
}
Переменную с нулевым значением не нужно инициализировать, прежде чем ее можно будет использовать. Он инициализируется как null
по умолчанию:
void main() {
String? word;
print(word); // prints null
}
2.2.2. !
Добавление !
к любой переменной e
вызовет ошибку времени выполнения, если e
значение равно null, и в противном случае преобразует его в v
значение, не равное нулю .
void main() {
int? e = 5;
int v = e!; // v is non-nullable; would throw an error if e were null
String? word;
print(word!); // throws runtime error if word is null
print(null!); // throws runtime error
}
2.3. late
Ключевое late
слово можно использовать для обозначения переменных, которые будут инициализированы позже, т.Е. Не При их объявлении, а при обращении к ним. Это также означает, что у нас могут быть ненулевые поля экземпляра, которые инициализируются позже:
class ExampleState extends State {
late final String word; // non-nullable
@override
void initState() {
super.initState();
// print(word) here would throw a runtime error
word = 'Hello';
}
}
Обращение word
к нему до его инициализации приведет к ошибке времени выполнения.
2.3.1. late final
Конечные переменные теперь также могут быть помечены с опозданием:
late final int x = heavyComputation();
Здесь heavyComputation
будет вызываться только один раз x
, когда будет получен доступ. Кроме того, вы также можете объявить a late final
без инициализатора, что то же самое, что иметь только late
переменную, но ее можно назначить только один раз.
late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden
Обратите внимание, что теперь будут оцениваться все переменные верхнего уровня или статические переменные с инициализатором late
, независимо от того, являются ли они final
.
2.4. required
Ранее аннотация ( @required
), теперь встроенная в качестве модификатора. Это позволяет пометить любой именованный параметр (для функций или классов) как required
, что делает их ненулевыми:
void allowed({required String word}) => null;
Это также означает, что если параметр должен быть ненулевым, он должен быть помечен как или иметь значение по умолчанию required
:
void allowed({String word = 'World'}) => null;
void forbidden({int x}) // compile-time error because x can be null (unassigned)
=>
null;
Любой другой именованный параметр должен иметь значение null:
void baz({int? x}) => null;
2.5. ?[]
?[]
Для оператора индекса был добавлен оператор, учитывающий значение null []
:
void main() {
List<int>? list = [1, 2, 3];
int? x = list?[0]; // 1
}
См. Также Эту статью о синтаксическом решении.
2.5.1. ?..
У каскадного оператора теперь также есть новый оператор, поддерживающий нуль: ?..
.
Это приводит к тому, что следующие каскадные операции выполняются только в том случае, если получатель не равен null. Следовательно, ?..
должен быть первым каскадным оператором в каскадной последовательности:
void main() {
Path? path;
// Will not do anything if path is null.
path
?..moveTo(3, 4)
..lineTo(4, 3);
// This is a noop.
(null as List)
?..add(4)
..add(2)
..add(0);
}
2.6. Never
Следующее объяснение отстой. Прочитайте «Сверху и снизу» из «Понимание нулевой безопасности» для хорошего понимания.
Чтобы избежать путаницы: это не то, о чем разработчикам нужно беспокоиться. Я хочу упомянуть об этом для полноты картины.
Never
это будет тип, подобный ранее существовавшему Null
(не null
), определенному в dart:core
. Оба этих класса не могут быть расширены, реализованы или смешаны, поэтому они не предназначены для использования.
По сути, Never
означает, что ни один тип не разрешен и Never
сам по себе не может быть создан.
Ничто, кроме Never
in a List<Never>
, не удовлетворяет ограничению общего типа списка, что означает, что он должен быть пустым. List<Null>
, однако, может содержать null
:
// Only valid state: []
final neverList = <Never>[
// Any value but Never here will be an error.
5, // error
null, // error
Never, // not a value (compile-time error)
];
// Can contain null: [null]
final nullList = <Null>[
// Any value but Null will be an error.
5, // error
null, // allowed
Never, // not a value (compile-time error)
Null, // not a value (compile-time error)
];
Пример: компилятор сделает вывод List<Never>
для пустого const List<T>
.
Never
насколько я понимаю, не должен использоваться программистами. (Я был неправ).
3. Подробнее
Вы можете прочитать официальную статью о безопасности звукового нуля.
Кроме того, как упоминалось в начале, вы можете играть с ним на DartPad.
Комментарии:
1. Можете ли вы привести несколько сценариев, в которых
Never
можно использовать?2. На самом деле мы решили использовать «?[]» для оператора индекса с нулевым значением вместо «?.[]». Последнее немного сложнее грамматически, но это то, чего хотят пользователи.
3. @RamsesAldama я кое-что добавил. В спецификации, на которую я ссылался, упоминается больше.
4. Следует отметить, что
late final
для переменной-члена или экземпляра выполняется проверка только во время выполнения. Это невозможно проверить во время разработки или во время компиляции из-за проблемы с остановкой. Таким образом, вы не получите помощи IDE с этим.5. Есть ли способ скомпилировать код сейчас с ненулевыми значениями по умолчанию, или Dartpad — единственное место, где это можно попробовать?
Ответ №2:
Если вы хотите, чтобы это поле было обязательным, используйте ключевое слово required . В противном случае вам нужно только добавить «?». Вот так:
const phonefield({
Key? key, required this.onchanged,
}) : super(key: key);
final ValueChanged<String>onchanged;