Не обнуляемый (по умолчанию)
В настоящее время эксперимент по ненулевому (по умолчанию) можно найти по адресу 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
сам не может быть создан.
Ничто, кроме Never
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
Насколько мне известно, программисты не должны использовать их.
Never
можно использовать?late final
переменная-член или экземпляр проверяет только во время выполнения. Это невозможно проверить во время разработки или компиляции из-за проблемы остановки. Таким образом, вы не получите помощь IDE с этим.