JSpecify 1.0.0 发布与 Java 空值标记提案
本周,JSpecify 项目发布了 1.0.0 版本,主要关注提供类型使用注解来指示静态类型的空值状态。
与此相关的是,最近公开的 Draft JEP 8303099 讨论了空值限制(Null-Restricted)和可空(Nullable)类型,旨在为 Java 语言引入可选的空值标记功能。该提案的目标是为类型使用添加标记,而不仅仅是注解,以指定该类型的允许值集是否包含 null
。
空值标记的三种形式
根据当前提案,对于类型 Foo
,有三种使用方式:
Foo!
表示空值限制(Null-Restricted),即该类型的允许值不包含null
。Foo?
表示可空(Nullable),即该类型的允许值明确包含null
。Foo
不指定是否允许null
,这是默认形式,以确保现有代码的编译行为不变。
空值转换
该功能引入了空值转换(类似于扩展和拆箱转换),允许以下赋值:
Foo!
到Foo?
Foo!
到Foo
Foo?
到Foo
Foo
到Foo?
这些转换表示约束的放宽,例如任何空值限制的值都可以表示为可空类型。此外,还存在窄化空值转换,如 Foo?
到 Foo!
和 Foo
到 Foo!
,这些转换可能导致运行时错误,如尝试将 null
从 Foo?
加载到 Foo!
中。处理这些情况时,编译器会发出警告(而非错误),并在运行时检查是否违反空值限制,若违反则抛出 NullPointerException
。
复杂情况与泛型
在处理泛型时,编译器可能会遇到类型参数的空值状态与其声明边界不一致的情况。引入空值标记提供了额外的编译时安全性,并允许逐步采用这些标记——首先定义类型的空值状态,然后逐步消除编译时警告。
Kevin Bourrillion 的访谈
InfoQ 采访了 Kevin Bourrillion(Google 核心库团队创始人,现为 Oracle Java 语言团队成员),以获取更多项目细节。
InfoQ: 请介绍你在 Java 空值处理方面的背景。
Kevin Bourrillion: 我是 JSpecify 组的联合创始人,主要负责推动复杂设计决策达成共识。尽管现在加入了 Oracle,我仍以类似的方式参与其中。
InfoQ: 该 JEP 与 JSpecify 的工作有何重叠?
Bourrillion: 最终,Java 将支持空值标记。JSpecify 已经精确地定义了注解的语义,这意味着项目可以顺利从 JSpecify 迁移到语言级别的空值标记,且这一迁移过程可以高度自动化。
InfoQ: 该 JEP 与 Java 5 引入泛型的方式是否类似?这是一个主要在编译时处理的机制,字节码级别会被擦除,对吗?
Bourrillion: 是的,有相似之处。类型擦除和“堆污染”是不幸的妥协,但也使得该功能易于采用。空值污染将长期存在,但这是可以接受的。与泛型类型信息一样,空值注解在运行时可通过反射获取,但不参与运行时类型检查。
InfoQ: 空值限制对 Project Valhalla 也很重要,你能否分享该 JEP 与 Valhalla 工作的交互?
Bourrillion: 这实际上是同一个问题,Valhalla 将基于该 JEP 草案进行构建。知道哪些值不能为 null
将帮助虚拟机优化间接引用。
InfoQ: 在中长期内,JSpecify 应能为 Java 提供语言级别空值支持的入口,类似于其与 Kotlin 空值支持的对接。你建议读者如何开始采用 JSpecify?
Bourrillion: JSpecify 的 jar 文件已发布 1.0.0 版本,但规范仍在微调中。如果今天为代码库添加大量注解,代码不会突然停止编译,但在规范修订后可能需要调整一些注解。如果清理现有警告耗时过长,广泛使用 @SuppressWarnings
是一个合理的选择,可以逐步清理。最重要的是,新代码将立即开始接受检查。
InfoQ: 谢谢!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。