随着 Flutter 1.20 正式版的发布,Dart 2.9 中的 null safety
已经可以是试验性使用,所以本篇翻译将介绍 Dart 中的 null safety
是什么。
对于 Dart 团队而言今天是 null safety
技术预览的一个重要里程碑,null safety
可以帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能,现在 null safety
的早期技术预览已经发布,期待大家的使用反馈。
这篇文章将介绍 Dart 小组推进 null safety
的计划,也从不同的途径解释了,为什么各种语言会需要我们所说的声明空安全。
为什么需要 null safety
Dart 是一种类型安全的语言,这意味着当开发者获取某种类型的变量时,编译器可以保证它是该类型,但是类型安全本身不能保证变量不是 null
。
Null errors 非常常见的问题,在 GitHub上 可以搜索到成千上万由于 null
导致 Dart 代码出现异常的问题,甚至有成千上万的 commits 试图解决这些问题。
例如你尝试查看是否可以在以下示例代码中发现 nullability 问题:
void printLengths(List<File> files) {
for (var file in files) {
print(file.lengthSync());
}
}
复制代码
如果通过 null
调用此函数肯定会失败,但是还要考虑第二种情况:
void main() {
// Error case 1: passing a null to files.
printLengths(null);
// Error case 2: passing list of files, containing a null item.
printLengths([File('filename1'), File('filename2'), null]);
}
复制代码
而空安全功能可以使该问题消失了:
使用 null
安全,开发者就可以放心地对代码进行推导,不再有讨厌的运行时 null
引用错误。相反开发者可以在编写代码时就发现静态错误。
可靠的 null safety
Dart 的 null safety 是可靠的,这意味着 Dart 可以 100% 确保,在上面的示例中 files
列表及其中的元素不能为 null
。当 Dart 分析代码并确定某个变量不可为空时,该变量将始终不能为空:如果你在 debugger 模式检查正在运行的代码,你将会发现 non-nullability 会在运行时保留。
Dart 的 null safety
可靠还具有另一个受欢迎的作用:可以让程序变得可以更小,更快。因为 Dart 可以确定 files
永远不会 null
,所以 Dart 可以优化执行。例如 Dart 提前(AOT)编译可以生成更小、更快的本机代码,因为当知道变量不会为空时,不需要添加对 null
的检查。
我们初步已经看到了一些非常有希望的结果,例如在 Flutter framework 模拟渲染模式的 microbenchmark 测试中看到了19%的性能提升。
设计原则
在开始针对 null safety
的详细设计之前,Dart 团队定义了以下三个核心原则:
-
默认情况下不可为空,除非开发者明确告知 Dart 变量可以为
null
,否则它将认为该变量不可为空。选择这个作为默认选项,因为我们发现 non-nullable 是迄今为止 API 中最常见的选择。 -
逐步采用,因为还有有很多 Dart 代码需要修改,必须把它们逐步迁移到
null safety
。在同一项目中应该可以包含null safety
代码和non-null-safe
代码,另外我们还将提供工具来帮助开发者进行迁移。 -
完全可靠,如上所述 Dart 的
null safety
是可靠的,将整个项目和依赖项迁移到null
安全之后,将获得稳健性带来的全部好处。
声明变量的 null safety
核心语法很简单,提供一些不同方式来声明 non-nullable 变量,并且默认值是不可为空的,因此这些声明看起来和之前好像一样,但是它们的含义发生了变化。
// In null-safe Dart, none of these can ever be null.
var i = 42;
final b = Foo();
String m = '';
复制代码
Dart 将确保绝不会分配给上述变量任何 null 值。如果尝试执行 i = null
,则会出现静态分析错误和红色的弯曲提示,并且无法继续编译。
如果开发者希望变量可为空,如下所示则可以使用?
:
// These are all nullable variables.
int? j = 1; // Can be null later.
final Foo? c = getFoo(); // Maybe the function returns null.
String? n; // Is null at first. Can be null at any later time, too.
复制代码
另外也可以将 ?
在运用到其他需要的地方:
// In function parameters.
void boogie(int? count) {
// It's possible that count is null.
}
// In function return values.
Foo? getFoo() {
// Can return null instead of Foo.
}
// Also: generics, typedefs, type checks, etc.
// And any combination of the above.
复制代码
但是,我们是希望你可以不需要使用到 ?
,因为绝大多数类型都是可以不必为空的。
使 null safety 更简易使用
Dart 开发小组正在努力使 null safety
尽可能方便使用,例如下面的代码,该代码 if
用于检查空值:
void honk(int? loudness) {
if (loudness == null) {
// No loudness specified, notify the developer
// with maximum loudness.
_playSound('error.wav', volume: 11);
return;
}
// Loudness is non-null, let's just clamp it to acceptable levels.
_playSound('honk.wav', volume: loudness.clamp(0, 11));
}
复制代码
这里可以看到 Dart 已经足够智能,以至于在我们传递该 if
语句时,loudness
变量其实已经保证不会为 null
。 因此 Dart 让我们调用该 clamp()
方法而无需进行判空。这种便利是通过 Flow analysis
的方法来实现:Dart分析器像执行代码一样浏览你的代码,自动找出有关代码的其他信息。
这是还有另一个示例,它显示了 Dart 可以确保变量为非 null
的情况,因为我们总是为其分配一个非 null
值:
int sign(int x) {
// The result is non-nullable.
int result;
if (x >= 0) {
result = 1;
} else {
result = -1;
}
// By this point, Dart knows the result cannot be null.
return result;
}
复制代码
如果开发者删除了上述部分逻辑(例如,通过删除 result = -1;
),则 Dart 无法保证 result
为非 null
,所以开发者将得到一个静态错误,并且代码会无法编译。
Flow analysis
仅在函数内部起作用,如果你有全局变量或类字段,则 Dart 无法保证何时为其分配什么值,Dart 也无法为整个应用程序流程建模。因此,当你在第一次读取变量之前,就知道变量将为非空时,可以使用 late
关键字声明,但是不能立即对其进行初始化。
class Goo {
late Viscosity v;
Goo(Material m) {
v = m.computeViscosity();
}
}
复制代码
请注意 v
它是非空的,尽管它开始时未初始化,但是 Dart 相信开发者不会在没有 v
赋值之前尝试读取它,所以代码可以正确编译。
null safety 是向后兼容
Dart 团队已经进行了一年多的工作,以确保 null safety
的技术预览可靠。自从我们引入 Dart 2 以来,这是 Dart 语言最大的功能添加,但这并不是一个重大变化。现有代码可以使用null safety
代码,反之亦然。即使在提供 null safety
之后,它也将是一项可选功能,开发者可以在准备就绪时采用它,你现有的代码将继续运行而无需更改。
我们最近迁移了 Dart 的核心库,以完全使用 null safety
。作为 null safety
是向后兼容的一个示例,我们替换了现有的核心库,而在 Dart 和 Flutter 测试环境中运行的现有测试和测试应用程序没有任何 break。
我们甚至将许多新的核心库直接投放给 Google 的内部客户,这些客户直接运行进入了他们的生产代码库。我们计划迁移所有软件包和应用程序,以在功能启动时使用 null safety
,希望你也能这样做。
null safety 路线图
我们计划分三步逐步推出无效安全性:
-
技术预览 :可在 Dart 的 dev channel 中找到,因为此刻仍可能发生未知变化,因此暂时不要在生产代码中使用
null safety
。 -
Beta版 :在 Dart 的 beta channel 中将提供
null safety
,该功能将非常接近预期的最终版本。如果拥有 pub.dev 软件包或插件,则可以开始迁移,但现在还不应该将其发布为稳定版本。 -
稳定发布 :每个开发者都可以正常使用
null safety
,因此建议你将迁移的软件包和插件发布为稳定版本。
如果一切顺利,我们计划在年底之前发布 null safety
到 stable,从现在开始我们将添加工具来帮助您使用 null safety
,包括:
- 迁移工具可支持你自动执行迁移升级,将现有软件包和应用程序迁移到
null safety
; - 增加 pub.dev 上的标签,因此你可以判断软件包是否支持
null safety
; -
pub outdated 命令的扩展,支持支持查找
null safety
依赖关系的最新版本;
现在就试试
现在尝试 null safety
最快的方法是通过 nullsafety.dartpad.dev,这是启用了空安全性的 DartPad 版本,打开 “Learn with Snippets” 下拉列表找到一系列学习例子,这些学习例子介绍了新的语法和 null safety
的基础知识。
另外我们也有文档和产品更多的发布计划:
我们很高兴为 Dart 带来 null safety
,可靠的 null safety
是 Dart 的一项独特功能,可帮助开发者编写更少容易出错的代码并获得更好的性能,我们同时也希望大家可以在技术预览版中试用该功能,并通过问题跟踪器向我们提供反馈。
最后,个人的额外提醒,目前在根目录的 analysis_options.yaml
添加如下配置就可以开启 null safety
,另外 Flutter 需要 dart sdk 2.9
。
analyzer:
enable-experiment:
- non-nullable
写在最后
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料免费分享出来。
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。包含知识脉络 + 诸多细节,由于篇幅有限,下面只是以图片的形式给大家展示一部分。
【Android学习PDF+学习视频+面试文档+知识点笔记】
【Android高级架构视频学习资源】
Android部分精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
【Android进阶学习视频】、【全套Android面试秘籍】可以简信我【学习】查看免费领取方式!
网友评论