Dart 中的空安全是什么?
What is Null Safety in Dart?
我听说了新的 Dart 空安全语言特性 (NNBD),目前是“'non-nullable' 实验”。它应该默认引入 non-nullable.
特征规范可以是found here and the language GitHub issue here。
它是如何工作的,我可以在哪里试用?
1。空安全/不可空(默认)
空安全/不可空(默认),简称 NNBD,功能目前可以在 nullsafety.dartpad.dev.
找到
请记住,您可以阅读 full spec here and full roadmap here. Now, sound null safety has also been officially announced for Dart。
2.1。默认情况下不可为空是什么意思?
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
}
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
}
一个nullable变量在使用前不需要初始化。默认初始化为null
:
void main() {
String? word;
print(word); // prints null
}
2.2.2。 !
将 !
附加到任何变量 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
}
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
被访问时调用。此外,您还可以声明一个没有初始化器的 late final
,这与只有一个 late
变量相同,但它只能被赋值一次。
late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden
请注意,所有 top-level 或 static 带有初始值设定项的变量现在都将被评估 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;
任何其他命名参数必须 可为空:
void baz({int? x}) => null;
2.5。 ?[]
为索引运算符添加了空感知 ?[]
运算符 []
:
void main() {
List<int>? list = [1, 2, 3];
int? x = list?[0]; // 1
}
另见 this article about the syntax decision。
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
下面的解释很烂。阅读 "Top and bottom" from "Understanding null safety" 好书。
为避免混淆:这不是开发人员必须担心的事情。为了完整起见,我想提一下。
Never
将成为 dart:core
中定义的先前存在的 Null
类型( 而非 null
) .这两个 类 都不能扩展、实现或混入,因此不打算使用它们。
本质上,Never
意味着不允许类型,Never
本身不能被实例化。
只有 List<Never>
中的 Never
满足列表的通用类型约束,这意味着它 必须是 empty.然而,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)
];
示例:编译器将为 empty const List<T>
.
推断 List<Never>
就我而言,Never
不应该被程序员使用。 (I was wrong).
3。了解更多
您可以阅读official article on sound null safety。
此外,如开头所述,您可以 play with it on DartPad.
如果您需要此字段,请使用 required 关键字。否则,您只需添加“?”。像这样:
const phonefield({
Key? key, required this.onchanged,
}) : super(key: key);
final ValueChanged<String>onchanged;
我听说了新的 Dart 空安全语言特性 (NNBD),目前是“'non-nullable' 实验”。它应该默认引入 non-nullable.
特征规范可以是found here and the language GitHub issue here。
它是如何工作的,我可以在哪里试用?
1。空安全/不可空(默认)
空安全/不可空(默认),简称 NNBD,功能目前可以在 nullsafety.dartpad.dev.
找到请记住,您可以阅读 full spec here and full roadmap here. Now, sound null safety has also been officially announced for Dart。
2.1。默认情况下不可为空是什么意思?
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
}
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
}
一个nullable变量在使用前不需要初始化。默认初始化为null
:
void main() {
String? word;
print(word); // prints null
}
2.2.2。 !
将 !
附加到任何变量 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
}
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
被访问时调用。此外,您还可以声明一个没有初始化器的 late final
,这与只有一个 late
变量相同,但它只能被赋值一次。
late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden
请注意,所有 top-level 或 static 带有初始值设定项的变量现在都将被评估 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;
任何其他命名参数必须 可为空:
void baz({int? x}) => null;
2.5。 ?[]
为索引运算符添加了空感知 ?[]
运算符 []
:
void main() {
List<int>? list = [1, 2, 3];
int? x = list?[0]; // 1
}
另见 this article about the syntax decision。
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
下面的解释很烂。阅读 "Top and bottom" from "Understanding null safety" 好书。
为避免混淆:这不是开发人员必须担心的事情。为了完整起见,我想提一下。
Never
将成为 dart:core
中定义的先前存在的 Null
类型( 而非 null
) .这两个 类 都不能扩展、实现或混入,因此不打算使用它们。
本质上,Never
意味着不允许类型,Never
本身不能被实例化。
只有 List<Never>
中的 Never
满足列表的通用类型约束,这意味着它 必须是 empty.然而,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)
];
示例:编译器将为 empty const List<T>
.
推断 List<Never>
就我而言,Never
不应该被程序员使用。 (I was wrong).
3。了解更多
您可以阅读official article on sound null safety。
此外,如开头所述,您可以 play with it on DartPad.
如果您需要此字段,请使用 required 关键字。否则,您只需添加“?”。像这样:
const phonefield({
Key? key, required this.onchanged,
}) : super(key: key);
final ValueChanged<String>onchanged;