Не нульовий (за замовчуванням)
Ненульовий (за замовчуванням) експеримент наразі можна знайти на сайті nullsafety.dartpad.dev .
Майте на увазі, що ви можете прочитати повну специфікацію тут, а повну дорожню карту тут .
Що означає ненульове значення за замовчуванням?
void main() {
String word;
print(word); // illegal
word = 'Hello, ';
print(word); // legal
}
Як ви бачите вище, зміна, яка за замовчуванням є ненульовою , означає, що кожна змінна, яка оголошується зазвичай, не може бути 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є нульовою, і іншим чином перетворить її в ненульове значення 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
}
Дивіться також цю статтю про рішення синтаксису .
?..
Оператор каскадний тепер має новий нуль обізнаний оператор: ?...
Це призводить до виконання наступних операцій каскаду, лише якщо одержувач не є нульовим . Таким чином, ?..повинен бути першим оператором каскаду в послідовності каскаду:
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його неможливо створити.
Нічого , крім Neverпротягом 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не передбачається використовувати програмістами, наскільки я переживаю.
Neverїх можна використовувати?