必须配置 AnnotatedPackages 或 OnlyNullMarked 之一,否则无法区分已注解与未注解代码;Dagger、AutoValue 等生成的代码需用 -XepExcludedPaths 排除,否则生成代码报错会阻断构建;Lombok 与 Error Prone 存在兼容问题,1.18.34 之前版本需在 lombok.config 开启 addLombokGeneratedAnnotation
NullAway
uberMIT活跃维护在编译期静态检查 Java 代码中的空指针隐患,构建开销很低,能提前发现 NPE 而不是等运行时崩溃。
A tool to help eliminate NullPointerExceptions (NPEs) in your Java code with low build-time overhead
plugins { id "net.ltgt.errorprone" version "<plugin version>" }
dependencies { errorprone "com.uber.nullaway:nullaway:<NullAway version>"
api "org.jspecify:jspecify:1.0.0"
errorprone "com.google.errorprone:error_prone_core:<Error Prone version>" }README 未说明硬件要求;需用 JDK 17 或更高版本构建,并依赖 Error Prone 2.36.0 或更高版本
基于注解的 Java 空指针检查工具,作为 Error Prone 插件在每次构建时低开销运行
在编译期静态检查 Java 代码中的空指针隐患,构建开销很低,能提前发现 NPE 而不是等运行时崩溃。
核心能力
- 作为 Error Prone 插件运行,构建开销通常低于 10%
- 基于 @Nullable 注解做类型局部检查,拦截空指针解引用
- 默认视参数、返回值、字段为非空,需显式标注可空
- 支持 JSpecify、AndroidX、JetBrains 等多种可空注解
- 可用 AnnotatedPackages 或 OnlyNullMarked 区分已注解代码
容易踩的坑
- v0.14.3NullAway 0.14.3最新正式版2026-10-22 · 2026-10-22 22:19 UTC
Many bug fixes in this release. Thanks especially to @vlsi and @subhramit for all the contributions! As they are now committers, their many contributions are not credited individually below.
Two bug fixes of note are #2005 and #2003, which may lead to new warnings even outside JSpecify mode.
We have also added initial support for
@PolyNullin library models in #1813. We will continue to enhance this support, but it should be helpful in removing some false positives fromJSpecifyExperimentalmode.- Allow configuring suppression-remover source encoding (#1894)
- Compute a call's executable type once per generic inference by @ericschlarmann (#1896)
- Skip nested annotation restoration from raw source types (#1898)
- Refine argument nullness after Objects.equals calls by @Beluomini (#1900)
- Keep nullable map values nullable after
containsKey(#1902) - Read nullable fields from JSpecify JDK models into astubx (#1901)
- Avoid initialization-check crashes in
@NullMarkedconstructors by @ZedingZhang (#1929) - Store types of implicit lambda parameters during inference (#1930)
- Compute erasure before binary promotion (#1935)
- Preserve captured type variables during constraint generation (#1941)
- Cache conversion of MethodSymbols to MethodRefs (#1964)
- Restore nullness annotations from implicit wildcard bounds (#1967)
- Use call-specific generic inference variables (#1965)
- Account for nullable map values in getOrDefault (#1985)
- Fix crash in map callbacks after an unmarked or suppressed filter predicate (#1996)
- Strip reference casts when computing access paths (#1997)
- Handle negated null checks in multi-argument boolean contracts (#1998)
- Skip parentheses around a lambda or method reference passed to unmarked code (#1999)
- Fix crash on method references to annotated array types (#2000)
- Skip a lower-bounded wildcard when restoring annotations on a call type (#2001)
- Strip parentheses before handlers match stream callbacks and boolean literals (#2002)
- Check explicitly typed lambda parameters against unmarked interface methods (#2003)
- Add PolyNull library models and apply them to Optional.orElseGet and Map.computeIfAbsent (#1813)
- Report unboxing of a @Nullable operand inside a primitive conditional expression (#2005)
- Report unboxing of a @Nullable result inside a primitive switch expression (#2006)
- Fix crash on null-marked code nested in unmarked lambdas and local classes (#2007)
- Strip parentheses before the futures and gRPC handlers match an argument (#2010)
- Recognize boolean constants behind a cast in EnsuresNonNullIf and stream filters (#2011)
- Treat a parenthesized lambda or method reference like an unwrapped one in generics checks (#2012)
- Recognize a cast null check in a boolean contract argument (#2013)
- Keep the access path of a map key, call argument, or array index cast to its own type (#2014)
- Check nested nullness of explicitly typed lambda parameters (#2015)
- Keep distinct constant map keys and call arguments on distinct access paths (#2016)
- Apply conditional library return models to method references (#1968)
- Reject conflicting fixed parameter models regardless of PolyNull (#2025)
- Keep the boxing wrapper in the identity of a variable map key (#2017)
- Maintenance
- Report diff coverage after each test run, quoting the changed lines the run did not execute and the branches it took one way, and writing the whole report to
<module>/build/reports/diff-coverage/test.txt; a// diff-coverage: ignorecomment moves a line out of that list without changing the counts (#1833) - Update Gradle to 9.8.1 (#1915, #1959)
- Update build plugins (#1878, #1917, #1918, #1969)
- Update library and test dependencies (#1884, #1885, #1921, #1922, #1923, #1925, #1927, #1970, #1973, #1974, #1976, #1977, #1978, #1981)
- Update GitHub Actions (#1920, #1928, #1979)
- Decouple build, lint and test CI checks (#1906)
- Update instruct
- Report diff coverage after each test run, quoting the changed lines the run did not execute and the branches it took one way, and writing the whole report to
- v0.14.2NullAway 0.14.22026-09-25 · 2026-09-25 02:40 UTC
查看更新说明
This release adds
JSpecifyUnrecognizedAnnotationLocation, an opt-in check that reports nullness annotations in locations JSpecify does not recognize. See further documentation on the wiki: https://github.com/uber/NullAway/wiki/JSpecify-Support#jspecifyunrecognizedannotationlocation-checkerNot packaged in the release, but we have also added a script to remove unnecessary NullAway suppressions to the repo: https://github.com/uber/NullAway/tree/master/scripts/nullaway-suppression-remover We expect this to be a useful script to run after NullAway updates, to check if NullAway improvements make some suppressions unnecessary.
Also of note, NullAway now more precisely detects whether the JDK used for the build properly supports reading type use annotations from bytecode, particularly important for JSpecify mode. This may lead NullAway to crash on certain build configurations that were unsupported before but we were not detecting precisely.
Beyond the above, this release includes further fixes for JSpecify mode and JSpecifyExperimental, especially for wildcard bounds and multi-dimensional arrays, along with other bug fixes.
Finally, we have re-enabled pushing snapshot builds to the Sonatype snapshots repository, which should ease testing NullAway versions between releases.
- Small optimization for stream handlers (#1774)
- Check wildcard generics handling flag for enhanced for loops (#1786)
- Cover anonymous class type arguments and modeled call-site returns by @vlsi (#1777)
- Write the full encoded byte array when serializing TSV/XML rows #1788 by @AzazelSensei (#1798)
- Fix @RequiresNonNull override error message to list the extra fields added by the child method by @Beluomini (#1751)
- Model Stream.takeWhile (+ reactive equivalents) like .filter by @jeffrey-easyesi (#1809)
- Add
JSpecifyUnrecognizedAnnotationLocation, an opt-in check that reports nullness annotations in locations JSpecify does not recognize by @vlsi (#1787) - Detect support for the addTypeAnnotationsToSymbol flag on JDK 17 / 21 (#1794)
- Fix
RequireExplicitNullMarkingdiagnostics repeating the check name, so a report no longer begins with[RequireExplicitNullMarking] [RequireExplicitNullMarking]by @vlsi (#1815) - Less conservative handling of raw array types (#1807)
- Fix crasher example from Caffeine (#1820)
- Add a script to remove unnecessary NullAway suppressions (#1803)
- Use LinkedHashMap / LinkedHashSet for determinism (#1796)
- Prevent classloader closure from interrupting stubx reads (#1830)
- Preserve non-null type variables through wildcard capture (#1837)
- Treat Maven plugin parameters as externally initialized by @ZedingZhang (#1848)
- Do not treat type variables from the caller as inference variables (#1824)
- Prevent recursion on self-referential wildcard bounds during inference by @adityaanikam (#1843)
- Avoid stale wildcard bounds after javac supertype inspection (#1849)
- Preserve contextual bounds for recursive wildcard captures (#1853)
- Restore effective wildcard upper bounds for invocation types (#1855)
- Preserve wildcard component types for array element assignments (#1857)
- Lazily allocate maps to detect wildcard cycles (#1888)
- Preserve all dimensions of multi-dimensional array creation expressions in JSpecify mode by @dbwiddis (#1711)
- Fix false positives for Spring SpEL expressions using
nullin comparisons by @Beluomini (#1750) - Apply covariant array subtyping at every dimension by @dbwiddis (#1889)
- Maintenance
- Remove outdated Spark test (#1772)
- Disable Jacoco by default (#1773)
- Document MethodRef overload signatures in the sample library model #1016 by @AzazelSensei (#1779)
- Migrate CoreTests to addSourceLines by @abdeltaehass (#1765)
- Fix builds from a linked git worktree by skipping installGitHooks by @vlsi (#1776)
- Add tests for JPA mixed access type and anonymous-class callbacks in Map.forEach by @Beluomini (#1752)
- Add test cas
- v0.14.1NullAway 0.14.12026-08-29 · 2026-08-29 23:40 UTC
查看更新说明
Several bug fixes in this release, particularly for
JSpecifyExperimental. Thanks to all who tested out this mode, reported bugs, and contributed! Please continue to test and send feedback. See the 0.14.0 release notes below for more information onJSpecifyExperimental.- Fix restoring nullness annotations from an unbounded wildcard to a captured type (#1717)
- JSpecify: support anonymous diamond classes by @subhramit (#1710)
- Fix attribute errors reported during dataflow (#1736)
- Apply library models to an overridden method type when checking overrides (#1722)
- Deduplicate generic inference failure warnings (#1741)
- Preserve nested nullness annotations through inheritance (#1742)
- Fix crash for generic qualifiers in method references (#1744)
- Cache immutable stubx library models (#1745)
- Allow nullable method references for void functions (#1747)
- Preserve nested nullness in enhanced-for variable types (#1748)
- Track nullable elements in enhanced-for dataflow (#1749)
- Honor ignored methods in null-marked library models (#1753)
- Avoid crashes on malformed
@Contractarity (fixes #1726) (#1756) - Inherit null-implies-null models across overrides (#1758)
- Fix inference for annotated type variable uses (#1759)
- Preserve contract unreachability across store joins (#1761)
- Prevent recursion on self-referential wildcard bounds (#1763)
- Work around pre-JDK-25 limitations in reading upper bounds from wildcard arguments in bytecode (#1764)
- Improve diagnostics for non-null type variable bounds (#1770)
- Fix override checks for method type variables bounded by a class type variable by @pivovarit (#1775)
- Maintenance
- Migrate FrameworkTests to
addSourceLinesby @abdeltaehass (#1712) - Attribute errors reported during dataflow to the right file by @vlsi (#1734)
- Disable CodeRabbit summaries (#1755)
- Add tests for overrides of
Collection.toArray(#1757) - Assert the full set of diagnostics for annotated type-variable uses by @vlsi (#1768)
- Document that a
nullImpliesNullmodel applies to overriding methods by @vlsi (#1771) - Add tests for the JSpecify nullness operator by @vlsi (#1767)
- Migrate FrameworkTests to
- v0.14.0NullAway 0.14.02026-08-21 · 2026-08-21 18:18 UTC
查看更新说明
This release has significant improvements to JSpecify support, including improved support for wildcards and integration of the standard library nullability annotations from https://github.com/jspecify/jdk. Much of this new support is gated behind a new
JSpecifyExperimentalflag, which is off by default. We disable the flag by default since this new support leads to many new errors in existing projects (mostly from the new JDK models), and because we need more real-world testing before enabling it by default. We encourage projects to enable theJSpecifyExperimentalflag (alongside JSpecify mode) and to report any issues that arise. We expect to turnJSpecifyExperimentalon by default in a future release.Aside from the above, you may observe some newly reported warnings in JSpecify mode, due to other checking improvements.
This release also removes the
LegacyAnnotationLocationsflag. Type-use annotations must now be placed correctly on qualified and array types, even outside JSpecify mode, see details here:https://github.com/uber/NullAway/wiki/JSpecify-Support#type-use-annotation-placement
Beyond the above, we had useful changes from a variety of new contributors; thanks so much!
- Remove LegacyAnnotationLocations flag (#1640)
- Handle signature-polymorphic calls in InvocationArguments (#1644)
- Fix handling of reference to method with
@Nullableparameter from library model (#1642) - Enable JSpecify JDK models (under a flag) (#1641)
- Enable JSpecify JDK models for regression tests (#1646)
- Fix bug with lambdas assigned to locals with a wildcard in their type (#1647)
- Add JSpecifyExperimental configuration flag (#1648)
- Improve wildcard mismatch diagnostics for identical-looking types (#1627)
- Enable inference failure warnings in experimental mode, and fix related bug in library models (#1649)
- Fix inference bug with generic instance methods (#1654)
- Fixes related to captured types and inference (#1655)
- Augment NullAway error / fix serialization for Annotator auto fix mode by @nimakarimipour (#1322)
- More consistent checks for captured types (#1662)
- Update to JSpecify 1.0.1 (#1665)
- Add test for issue 1671 (#1673)
- Test case and fix for issue 1672 (#1674)
- Fix subtype checking for nested captured types (#1663)
- Handle interaction of captured types and library models (#1666)
- Follow-on fix for restoring annotation on captured type wildcards (#1667)
- Fix subtle issue with invalid TreePaths (#1681)
- Honor @Contract when a @Nullable method is used as a method reference by @Eljees (#1679)
- Defensively thread path into NestedTypeVarSubstitutionRepairVisitor (#1683)
- More TreePath hardening (#1684)
- Harden TypeSubstitutionUtils type copies (#1669)
- Handle unbound wildcards and interactions with captured types better (#1668)
- Improve error messages involving capture variables (#1675)
- Update JarInfer to skip synthetic methods (#1686)
- Fix bug with storing library model type variable upper bounds (#1687)
- Fixes for processing return array types and multi-dimensional array types in AstubxGenerator (#1689)
- Pretty-print substituted types in bad-override error messages by @dbwiddis (#1697)
- Add test for issue 1693 (#1698)
- JSpecify: support annotated type arguments from an enclosing class by @dbwiddis (#1699)
- Fix crash in @Contract dataflow init for zero-argument methods by @dbwiddis (#1701)
- Support method-based conditional library model postconditions by @Shankar-v27 (#1677)
- Update JSpecify JDK astubx (#1702)
- JSpecify: preserve array element nullability through requireNonNull by @kamilkrzywanski (#1645)
- Add positive test cases for annotations on generic method return types by @dbwiddis (#1704)
- Add regression test for inner classes inheriting enclosing type arguments by @dbwiddis (#1703)
- Fix JSpecify false negative when override narrows method type variable bound by @arimu1 (#1682)
- Fix bug with library-modeled ret
- v0.13.8NullAway 0.13.82026-07-19 · 2026-07-19 03:10 UTC
查看更新说明
Mostly minor bug fixes. Note that NullAway now treats
Map.removeas returning@Nullable(#1623), which may lead to newly-reported warnings.- Quote syntax element references in error messages by @AryamannSingh7 (#1620)
- Fix stream nullability propagation through sorted() (Fixes #1538) by @dyrpsf (#1610)
- Add support for reactor by @subhramit (#1599)
- Fix issue with passing poly expression to a function parameter with type from library model (#1614)
- Fix crash with library model @Nullable being applied to wildcard (#1615)
- Add more modeling for Optional (#1613)
- Update lombok section at README.md by @jevanlingen (#1624)
- Model Map.remove as nullable by @codingkiddo (#1623)
- Better support for conditional expressions in JSpecify mode (#1608)
- Fix issue with reading annotations on record constructor parameters due to javac bug (#1636)
- Maintenance
- Update to Gradle 9.6.1 and AGP 9.3.0 (#1634)
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
NullAway: Fast Annotation-Based Null Checking for Java

NullAway is a tool to help eliminate NullPointerExceptions (NPEs) in your Java code. To use NullAway, first add @Nullable annotations in your code wherever a field, method parameter, or return value may be null. Given these annotations, NullAway performs a series of type-based, local checks to ensure that any pointer that gets dereferenced in your code cannot be null. NullAway is similar to the type-based nullability checking in the Kotlin and Swift languages, and the Checker Framework and Eradicate null checkers for Java.
NullAway is fast. It is built as a plugin to Error Prone and can run on every single build of your code. In our measurements, the build-time overhead of running NullAway is usually less than 10%. NullAway is also practical: it does not prevent all possible NPEs in your code, but it catches most of the NPEs we have observed in production while imposing a reasonable annotation burden, giving a great "bang for your buck."
Installation
Overview
NullAway requires that you build your code with JDK 17 or higher and Error Prone, version 2.36.0 or higher. See the Error Prone documentation for instructions on getting started with Error Prone and integration with your build system. The instructions below assume you are using Gradle; see the docs for discussion of other build systems. If you are building with JSpecify mode enabled, we recommend building with the most recent JDK available; see the wiki docs on JSpecify support for more details.
Gradle
Java (non-Android)
To integrate NullAway into your non-Android Java project, add the following to your build.gradle file:
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")
}
}
}
Let's walk through this script step by step. The plugins section pulls in the Gradle Error Prone plugin for Error Prone integration.
In dependencies, the first errorprone line loads NullAway, and the api line loads the JSpecify library which provides suitable nullability annotations, e.g., org.jspecify.annotations.Nullable. NullAway allows for any @Nullable annotation to be used, so, e.g., @Nullable from the AndroidX annotations Library or JetBrains annotations is also fine. The second errorprone line sets the version of Error Prone is used.
Finally, in the tasks.withType(JavaCompile) section, we pass some configuration options to NullAway. First check("NullAway", CheckSeverity.ERROR) sets NullAway issues to the error level (it's equivalent to the -Xep:NullAway:ERROR standard Error Prone argument); by default NullAway emits warnings. Then, option("NullAway:AnnotatedPackages", "com.uber") (equivalent to the -XepOpt:NullAway:AnnotatedPackages=com.uber standard Error Prone argument) tells NullAway that source code in packages under the com.uber namespace should be checked for null dereferences and proper usage of @Nullable annotations, and that class files in these packages should be assumed to have correct usage of @Nullable (see the docs for more detail). NullAway requires exactly one of the AnnotatedPackages or OnlyNullMarked configuration arguments to run, in order to distinguish between annotated and unannotated code. See the configuration docs for more details and other useful configuration options. For even simpler configuration of NullAway options, use the Gradle NullAway plugin. Finally, we show how to disable NullAway on test code, if desired.
We recommend addressing all the issues that Error Prone reports, particularly those reported as errors (rather than warnings). But, if you'd like to try out NullAway without running other Error Prone checks, you can use options.errorprone.disableAllChecks (equivalent to passing "-XepDisableAllChecks" to the compiler, before the NullAway-specific arguments).
Android
Versions 3.0.0 and later of the Gradle Error Prone Plugin no longer support Android. So if you're using a recent version of this plugin, you'll need to add some further configuration to run Error Prone and NullAway. Our sample app build.gradle file shows one way to do this, but your Android project may require tweaks. Alternately, 2.x versions of the Gradle Error Prone Plugin still support Android and may still work with your project.
Beyond that, compared to the Java configuration, the JSpecify dependency can be removed; you can use the androidx.annotation.Nullable annotation from the AndroidX annotation library instead.
Annotation Processors / Generated Code
Some annotation processors like Dagger and AutoValue generate code into the same package namespace as your own code. This can cause problems when setting NullAway to the ERROR level as suggested above, since errors in this generated code will block the build. Currently the best solution to this problem is to completely disable Error Prone on generated code, using the -XepExcludedPaths option added in Error Prone 2.1.3 (documented here, use options.errorprone.excludedPaths= in Gradle). To use, figure out which directory contains the generated code, and add that directory to the excluded path regex.
Note for Dagger users: Dagger versions older than 2.12 can have bad interactions with NullAway; see here. Please update to Dagger 2.12 to fix the problem.
JSpecify Mode / Guava
As of version 33.4.1, Guava uses JSpecify @NullMarked annotations for most of its packages, and hence NullAway will treat those packages as annotated by default. This treatment may lead to some false positives (due to handling of type variables), which can be mitigated by running NullAway in its (under-development) JSpecify mode. See the wiki docs on JSpecify support for more details.
Lombok
Unlike other annotation processors above, Lombok modifies the in-memory AST of the code it processes, which is the source of numerous incompatibilities with Error Prone and, consequently, NullAway.
We do not particularly recommend using NullAway with Lombok. However, NullAway encodes some knowledge of common Lombok annotations and we do try for best-effort compatibility. In particular, common usages like @lombok.Builder and @Data classes should be supported.
Since Lombok v1.18.34, Lombok adds @lombok.Generated by default to the methods/classes it generates. NullAway relies on this annotation to detect generated code within the in-memory Java AST and will ignore (i.e. not check) the implementation of this generated code, treating it as unannotated.
If you have explicitly disabled this behavior then NullAway will not correctly recognize Lombok-generated code, and compatibility may break.
If you are using a Lombok version earlier than 1.18.34, you must enable the following configuration option in an applicable lombok.config file:
lombok.addLombokGeneratedAnnotation = true
Code Example
Let's see how NullAway works on a simple code example:
static void log(Object x) {
System.out.println(x.toString());
}
static void foo() {
log(null);
}
This code is buggy: when foo() is called, the subsequent call to log() will fail with an NPE. You can see this error in the NullAway sample app by running:
cp sample/src/main/java/com/uber/mylib/MyClass.java.buggy sample/src/main/java/com/uber/mylib/MyClass.java
./gradlew build
By default, NullAway assumes every method parameter, return value, and field is non-null, i.e., it can never be assigned a null value. In the above code, the x parameter of log() is assumed to be non-null. So, NullAway reports the following error:
warning: [NullAway] passing @Nullable parameter 'null' where @NonNull is required
log(null);
^
We can fix this error by allowing null to be passed to log(), with a @Nullable annotation:
static void log(@Nullable Object x) {
System.out.println(x.toString());
}
With this annotation, NullAway points out the possible null dereference:
warning: [NullAway] dereferenced expression 'x' is @Nullable
System.out.println(x.toString());
^
We can fix this warning by adding a null check:
static void log(@Nullable Object x) {
if (x != null) {
System.out.println(x.toString());
}
}
With this change, all the NullAway warnings are fixed.
For more details on NullAway's checks, error messages, and limitations, see our detailed guide.
Support
Please feel free to open a GitHub issue if you have any questions on how to use NullAway. Or, you can join the NullAway Discord server and ask us a question there.
Contributors
We'd love for you to contribute to NullAway! Please note that once you create a pull request, you will be asked to sign our Uber Contributor License Agreement.
License
NullAway is licensed under the MIT license. See the LICENSE.txt file for more information.