NullAway 是一个帮助消除 Java 代码中的NullPointerException
(NPE) 的工具。要使用 NullAway,请首先在代码中字段、方法参数或返回值可能为null
位置添加@Nullable
注释。给定这些注释,NullAway 会执行一系列基于类型的本地检查,以确保代码中取消引用的任何指针都不能为null
。 NullAway 类似于 Kotlin 和 Swift 语言中基于类型的可空性检查,以及 Java 的 Checker Framework 和 Eradicate null 检查器。
NullAway速度很快。它是作为 Error Prone 的插件构建的,可以在代码的每个版本上运行。根据我们的测量,运行 NullAway 的构建时间开销通常低于 10%。 NullAway 也很实用:它不会阻止代码中所有可能的 NPE,但它会捕获我们在生产中观察到的大多数 NPE,同时施加合理的注释负担,从而“物有所值”。
NullAway 要求您使用 Error Prone 版本 2.14.0 或更高版本构建代码。请参阅 Error Prone 文档,了解有关 Error Prone 入门以及与构建系统集成的说明。以下说明假设您正在使用 Gradle;有关其他构建系统的讨论,请参阅文档。
要将 NullAway 集成到您的非 Android Java 项目中,请将以下内容添加到您的build.gradle
文件中:
plugins {
// we assume you are already using the Java plugin
id " net.ltgt.errorprone " version " <plugin version> "
}
dependencies {
errorprone " com.uber.nullaway:nullaway:<NullAway version> "
// Some source of nullability annotations; JSpecify recommended,
// but others supported as well.
api " org.jspecify:jspecify:1.0.0 "
errorprone " com.google.errorprone:error_prone_core:<Error Prone version> "
}
import net.ltgt.gradle.errorprone.CheckSeverity
tasks . withType( JavaCompile ) {
options . errorprone {
check( " NullAway " , CheckSeverity . ERROR )
option( " NullAway:AnnotatedPackages " , " com.uber " )
}
// Include to disable NullAway on test code
if (name . toLowerCase() . contains( " test " )) {
options . errorprone {
disable( " NullAway " )
}
}
}
让我们逐步浏览一下这个脚本。 plugins
部分引入了 Gradle Error Prone 插件以进行 Error Prone 集成。
在dependencies
中,第一个errorprone
行加载 NullAway, api
行加载 JSpecify 库,该库提供合适的可空性注释,例如org.jspecify.annotations.Nullable
。 NullAway 允许使用任何@Nullable
注释,因此,例如,来自 AndroidX 注释库或 JetBrains 注释的@Nullable
也可以。第二个errorprone
行设置使用的 Error Prone 版本。
最后,在tasks.withType(JavaCompile)
部分,我们将一些配置选项传递给NullAway。首先check("NullAway", CheckSeverity.ERROR)
将 NullAway 问题设置为错误级别(它相当于-Xep:NullAway:ERROR
标准 Error Prone 参数);默认情况下 NullAway 会发出警告。然后, option("NullAway:AnnotatedPackages", "com.uber")
(相当于-XepOpt:NullAway:AnnotatedPackages=com.uber
标准易错参数)告诉 NullAway com.uber
命名空间下的包中的源代码应该是检查 null 取消引用和@Nullable
注释的正确使用,并且应假定这些包中的类文件具有@Nullable
的正确使用(有关更多信息,请参阅文档 细节)。 NullAway 至少需要AnnotatedPackages
配置参数才能运行,以便区分带注释和未注释的代码。有关其他有用的配置选项,请参阅配置文档。要更简单地配置 NullAway 选项,请使用 Gradle NullAway 插件。最后,我们将展示如何在需要时在测试代码上禁用 NullAway。
我们建议解决 Error Prone 报告的所有问题,特别是那些报告为错误(而不是警告)的问题。但是,如果您想尝试 NullAway 而不运行其他容易出错的检查,则可以使用options.errorprone.disableAllChecks
(相当于在 NullAway 特定参数之前将"-XepDisableAllChecks"
传递给编译器)。
Gradle Error Prone 插件的 3.0.0 及更高版本不再支持 Android。因此,如果您使用的是此插件的最新版本,则需要添加一些进一步的配置才能运行 Error Prone 和 NullAway。我们的示例应用程序build.gradle
文件展示了执行此操作的一种方法,但您的 Android 项目可能需要调整。或者,2.x 版本的 Gradle Error Prone 插件仍然支持 Android,并且可能仍然适用于您的项目。
除此之外,与Java配置相比,可以删除JSpecify依赖;您可以使用 AndroidX 注释库中的androidx.annotation.Nullable
注释。
一些注释处理器(例如 Dagger 和 AutoValue)将代码生成到与您自己的代码相同的包命名空间中。当将 NullAway 设置为上面建议的ERROR
级别时,这可能会导致问题,因为生成的代码中的错误将阻止构建。目前解决此问题的最佳解决方案是使用 Error Prone 2.1.3 中添加的-XepExcludedPaths
选项在生成的代码上完全禁用 Error Prone(此处记录,在 Gradle 中使用options.errorprone.excludedPaths=
)。要使用,请找出哪个目录包含生成的代码,并将该目录添加到排除的路径正则表达式中。
Dagger 用户注意:2.12 之前的 Dagger 版本可能会与 NullAway 产生不良交互;见这里。请更新至 Dagger 2.12 以修复该问题。
与上述其他注释处理器不同,Lombok 修改了它处理的代码的内存中 AST,这是与 Error Prone 以及 NullAway 不兼容的根源。
我们不特别推荐将 NullAway 与 Lombok 一起使用。然而,NullAway 编码了常见 Lombok 注释的一些知识,我们确实尽力实现兼容性。特别是,应该支持@lombok.Builder
和@Data
类等常见用法。
为了让 NullAway 成功检测内存中 Java AST 中 Lombok 生成的代码,必须将以下配置选项作为适用lombok.config
文件的一部分传递给 Lombok:
lombok.addLombokGeneratedAnnotation = true
这会导致 Lombok 将@lombok.Generated
添加到它生成的方法/类中。 NullAway 将忽略(即不检查)此生成代码的实现,将其视为未注释的。
让我们通过一个简单的代码示例来看看 NullAway 如何工作:
static void log ( Object x ) {
System . out . println ( x . toString ());
}
static void foo () {
log ( null );
}
这段代码有缺陷:当调用foo()
时,后续对log()
调用将失败并出现 NPE。您可以通过运行以下命令在 NullAway 示例应用程序中看到此错误:
cp sample/src/main/java/com/uber/mylib/MyClass.java.buggy sample/src/main/java/com/uber/mylib/MyClass.java
./gradlew build
默认情况下,NullAway 假定每个方法参数、返回值和字段都是非 null 的,即永远不能为其分配null
值。在上面的代码中, log()
的x
参数被假定为非空。因此,NullAway 报告以下错误:
warning: [NullAway] passing @Nullable parameter 'null' where @NonNull is required
log(null);
^
我们可以通过允许将null
传递给log()
并使用@Nullable
注释来修复此错误:
static void log ( @ Nullable Object x ) {
System . out . println ( x . toString ());
}
通过这个注释,NullAway 指出了可能的 null 取消引用:
warning: [NullAway] dereferenced expression x is @Nullable
System.out.println(x.toString());
^
我们可以通过添加空检查来修复此警告:
static void log ( @ Nullable Object x ) {
if ( x != null ) {
System . out . println ( x . toString ());
}
}
通过此更改,所有 NullAway 警告均已修复。
有关 NullAway 检查、错误消息和限制的更多详细信息,请参阅我们的详细指南。
如果您对如何使用 NullAway 有任何疑问,请随时打开 GitHub 问题。或者,您可以加入 NullAway Discord 服务器并在那里向我们提问。
我们希望您能为 NullAway 做出贡献!请注意,创建拉取请求后,系统会要求您签署我们的 Uber 贡献者许可协议。
NullAway 已获得 MIT 许可。有关详细信息,请参阅 LICENSE.txt 文件。