Что такое нулевая безопасность в Dart?

#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;