Java的空限制与可空类型

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
  • FooFoo?

这些转换表示约束的放宽,例如任何空值限制的值都可以表示为可空类型。此外,还存在窄化空值转换,如 Foo?Foo!FooFoo!,这些转换可能导致运行时错误,如尝试将 nullFoo? 加载到 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: 谢谢!

阅读 10
0 条评论