Что такое обнуляемость в дротике (по умолчанию не обнуляется)?

27

Я слышал о новой функции языка безопасности ноль в Dart, которая в настоящее время является « экспериментом без значения NULL ». По умолчанию предполагается ввести ненулевое значение .

Спецификацию функции можно найти здесь, а язык GitHub - здесь .

Как это работает и где я могу попробовать?

creativecreatorormaybenot
источник

Ответы:

49

Не обнуляемый (по умолчанию)

В настоящее время эксперимент по ненулевому (по умолчанию) можно найти по адресу nullsafety.dartpad.dev .
Имейте в виду, что вы можете прочитать полную спецификацию здесь и полную дорожную карту здесь .


Что означает ненулевое значение по умолчанию?

void main() {
  String word;
  print(word); // illegal

  word = 'Hello, ';
  print(word); // legal
}

Как вы можете видеть выше, переменная, которая по умолчанию не имеет значения NULL , означает, что любая объявленная переменная не может быть null. Следовательно, любая операция, обращающаяся к переменной до ее присвоения, является недопустимой.
Кроме того, присвоение nullненулевой переменной также недопустимо:

void main() {
  String word;

  word = null; // forbidden
  world = 'World!'; // allowed
}

Как это поможет мне?

Если переменная не обнуляется , вы можете быть уверены, что она никогда не будет null. Из-за этого вам никогда не нужно проверять это заранее.

int number = 4;

void main() {
  if (number == null) return; // redundant

  int sum = number + 2; // allowed because number is also non-nullable
}

Помните

Поля экземпляра в классах должны быть инициализированы, если они не обнуляются:

class Foo {
  String word; // forbidden

  String sentence = 'Hello, World!'; // allowed
}

Смотрите lateниже, чтобы изменить это поведение.

Обнуляемые типы ( ?)

Вы можете использовать обнуляемые типы , добавив знак вопроса ?к типу переменной:

class Foo {
  String word; // forbidden

  String? sentence; // allowed
}

Обнуляемым переменная не должна быть инициализирована , прежде чем он может быть использован. Инициализируется как nullпо умолчанию:

void main() {
  String? word;

  print(word); // prints null
}

!

Присоединение !к любой переменной 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
}

late

Ключевое слово lateможет использоваться для маркировки переменных, которые будут инициализированы позже , то есть не тогда, когда они объявлены, а когда к ним обращаются. Это также означает, что у нас могут быть ненулевые поля экземпляров, которые будут инициализированы позже:

class ExampleState extends State {
  late String word; // non-nullable

  @override
  void initState() {
    super.initState();

    // print(word) here would throw a runtime error
    word = 'Hello';
  }
}

Доступ wordдо его инициализации вызовет ошибку времени выполнения.

late final

Конечные переменные теперь также могут быть помечены поздно:

late final int x = heavyComputation();

Здесь heavyComputationбудет вызываться только один раз, когда xосуществляется доступ. Кроме того, вы также можете объявить late finalбез инициализатора, который аналогичен наличию только lateпеременной, но он может быть назначен только один раз.

late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden

Обратите внимание, что все верхние или статические переменные с инициализатором теперь будут оцениваться late, независимо от того, являются ли они final.

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;

Любой другой именованный параметр должен быть обнуляемым :

void baz({int? x}) => null;

?[]

Нулевой ?[]оператор был добавлен для оператора индекса []:

void main() {
  List<int>? list = [1, 2, 3];

  int? x = list?[0]; // 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);
}

Never

Чтобы избежать путаницы: разработчикам не о чем беспокоиться. Я хочу упомянуть об этом для полноты картины.

Neverбудет тип, подобный ранее существовавшему Null( неnull ) определенному в dart:core. Оба эти класса не могут быть расширены, реализованы или смешаны, поэтому они не предназначены для использования.

По сути, Neverозначает, что ни один тип не разрешен и Neverсам не может быть создан.
Ничто, кроме Nevera, не 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Насколько мне известно, программисты не должны использовать их.

creativecreatorormaybenot
источник
5
Можете ли вы привести некоторые сценарии, где Neverможно использовать?
Рамзес Алдама
2
На самом деле мы решили использовать «? []» Для оператора индекса с нулевым значением вместо «?. []». Последнее немного сложнее грамматически, но это то, что хотят пользователи.
Великолепно
@RamsesAldama Я кое-что добавил. Спецификация, на которую я ссылаюсь, упоминает больше.
creativecreatorormaybenot
@muntiful Спецификация и эксперимент все еще устарели; спасибо за указание
creativecreatorormaybenot
1
Следует отметить, что late finalпеременная-член или экземпляр проверяет только во время выполнения. Это невозможно проверить во время разработки или компиляции из-за проблемы остановки. Таким образом, вы не получите помощь IDE с этим.
Грэм