Just a year ago I wrote about null checking in Java. It still makes sense to include parameters of methods and constructors with annotations regarding behavior null
(eg @NonNull
). However, support has gotten much better now, as version 1.0 was recently released JSpecify has appeared. I would like to use this for updates on the topic.
Advertisement
Hendrik Ebbers (@hendrikEbbers) is a Java Champion, member of the JCP Expert Group and has been recognized as a JavaOne Rockstar Speaker several times. With his own company Open Elements, Hendrik is currently helping to design the Hedera Hashgraph and make its services available to the public. Hendrik is a co-founder of JUG Dortmund and Cyberland and gives lectures and workshops on Java worldwide. His book “Mastering JavaFX 8 Controls” was published by Oracle Press in 2014. Hendrik actively works on open source projects such as JakartaEE or Eclipse Adoptium. Hendrik is a member of the AdoptOpenJDK TSC and the Eclipse Adoptium WG.
Extensive support for JSpecify
JSpecify is a Open Source Projectin which previous providers of void-handling annotations have finally come together to define a usable standard. These include Google, JetBrains, Meta, Microsoft and Oracle. JSpecify is a full-fledged module in the Java module system, has no dependencies of its own and, with only four annotations, provides everything you need to handle a modern Java project. null
to specify parameters. Example code using annotations might look like this:
static @Nullable String emptyToNull(@NonNull String x) {
return x.isEmpty() ? null : x;
}
static @NonNull String nullToEmpty(@Nullable String x) {
return x == null ? "" : x;
}
More code examples can be found here JSpecify User Guide,
However, simply adding the JSpecify annotation has little effect. The compiler continues to translate that code null
with one @NonNull-
Annotated parameters, and the translated code does not automatically throw exceptions at runtime.
Benefits of annotations
Among other things, the benefits of annotations can be seen in the interaction with the development environment. IntelliJ can recognize annotations and display warnings or errors for code that violates annotations. If you want to be safe and not allow code with such problems at all, you can use additional tools. Open source tools developed by Uber Zero away You can check for these annotations at build time and generate errors if the annotation definition is not met. If you add the whole thing to your Gradle or Maven build, an error will be automatically generated when compiling:
error: (NullAway) passing @Nullable parameter 'null' where @NonNull is required
myMethod(null);
^
With this toolchain you can make your code more robust NullPointerException
Avoid runtime.
Not a panacea
Now you don’t have to worry about it NullPointerExceptions
Creating? Unfortunately it’s not that easy. These solutions can only check your own code. If you have dependencies that do not use such annotations, you cannot know if you can use it as a parameter null
can be passed and what behavior it triggers. So it is still important to set variables in different places null
to check.
Anyone who develops libraries or code that is called by other projects cannot ensure that users follow the defined rules @NonNull
There are actually no annotated parameters null
Hand over. Therefore, it is important to always perform null checks when leaving context of your code – whether on your dependencies or on public APIs.
This is from OpenJDK java.util.Objects.requireNonNull(obj, message)
remains the method of choice. To always create meaningful exceptions, you must use the variant with the message parameter, otherwise the system… NullPointerException
Without messages. The whole thing looks like this for the public API:
public void setName(@NonNull String name) {
this.name = Objects.requireNonNull(name, “name must not be null”);
}
Anyone who works in a performance-critical environment should avoid using their own methods for checking. The JIT compiler handles it Objects.requireNonNull(...)
Through annotation @ForceInline
Specifically, it encapsulates all calls to the method directly into the calling method (inline) to optimize performance and stack size.
Best practices and next steps for the standard
It took a long time for the Java community to get to where it is today and have a clean and useful library with zero handling annotations. What jsr305 Which was started in 2006 and unfortunately dropped again soon after, after many problems with different types of annotations and implementations, before it could evolve into a de facto standard like SLF4J (Simple Logging Facade for Java).
JSpecify is clearly taking the right approach here. It would be great if a tool like NullAway became established and, with its ease of use and best practices, enabled almost every project to do better. null
to deal with. If you haven’t used annotations and tools like NullAway yet, you should give them a try. Now is the perfect time to start.
Comment: At the time of writing this post, OpenJDK is in parallel A new JEP Improved native support has been announced. Since the features discussed in the JEP will still take some time to find their way into the LTS version of OpenJDK, the resources and tools described here remain an obvious recommendation. However, the JEP provides enough aspects to take a closer look at it in a timely article.
(RME)