Не нульовий (за замовчуванням)
Ненульовий (за замовчуванням) експеримент наразі можна знайти на сайті 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
їх можна використовувати?