Nullpointer-free Java: Combining AOP with Modern Validation Standards

This article explores modern approaches to prevent common runtime exceptions in Java applications, particularly focusing on NullPointerExceptions. It demonstrates how combining Aspect-Oriented Programming (AOP) with validation standards like JSR-303, JSR-305, and JSR-308 can lead to more robust and maintainable code. The article compares different validation approaches, shows practical examples of annotation-based validation, and explains how AOP can be used to implement validation checks without violating the DRY principle. Special attention is given to method-level validation and the benefits of using these techniques in real-world applications.

3 Minutes reading time

Behold the masterpiece that AI hallucinated while reading this post:

"Little Java's Great Adventure: How to Make Null Monsters Go Away"

(after I fed it way too many marketing blogs and memes)

Created using DALL-E 3

AI-Generated: Little Java's Great Adventure: How to Make Null Monsters Go Away

Summary

For many years common runtime exceptions like NullPointerException or ClassCastException are haunting poor Java programmers. This often leads to repetitions in the source code for checking method arguments for nullness. This is violating the DRY(Don’t repeat yourself) principle. The Java Community Process created JSR-305 to address this issue. Unfortunately this JSR didn’t become part of the official Java languages, but tools like FindBugs or IDEs like IntelliJ are offering static code analysis to find such NullPointerExceptions at compile time.

Here is an example of a very leaky code:

public void doSomethingTheOldWay(String aParam) {
	if (aParam == null) {
		throw new IllegalArgumentException();
	}
	int length = aParam.length();
}

This method will definitely throw a IllegalArgumentException if null is passed as an argument. Ok, now lets take a look at the following code:

public String returnSomething() {
        return null;
}

public void compute() {
        int theLength = returnSomething().length();
}

This will for sure generate a NullPointerException. Those errors are hard to track. Even with static code analysis FindBugs or IntelliJ do not recognize the problem.

JSR-305

JSR-305 was designed to help with automatic detect detection. This JSR provides a set of common annotations to easy code analysis. Here is an example:

public void doSomethingWithParam(@Nonnull String aParam) {
        if (aParam == null) {
            throw new IllegalArgumentException();
        }
}

@Nonnull public String compute(@Nonnull String aParam) {
        return null;
}

The doSomethingWithParam() Method is annotated so it cannot accept a null as a parameter. The compute() Method is annotated that is does not accept a null parameter and even does not return a null value. Let''s see what IntellJ inspections are doing with the following example:

doSomethingWithParam(null);
compute("Mirko");

This will give you the following bug report:

jsr305

JSR-308

JSR-308 Annotations on Java Types is another approach to the problem mentioned above. It defines a language extension that will probably be added in Java 8. It includes a set of annotations like @NonNull, @Nullable and so on, and a checker framework as well. Unfortunately it provides some redundant annotations to JSR-303, hopefully this will be fixed.

JSR-303

JSR-303 Bean Validation API gives us the same set of annotations as JSR-305, Nullable, NotNull and so on. It also provides more like validations rules with patterns. JSR-305 was probably retired as JSR-303 or JSR-308 can do the same thing.

Very interesting is JSR-303 Version 1.1. It provides a new Method Level Validation. Details can be found here. The official release is planned for Q3/Q4 2012. IntelliJ does recognize Nullable or NotNull annotations, but not the NotNull annotations for method return value. Here is a bug report generated by IntelliJ for the above example with JSR-303 annotations:

jsr303

AOP

Checking method parameters for nullness and manual throwing of IllegalArgumentException is bloating the source code and also violating the DRY principle. We can use an around advice together with an AOP framework like AspectJ to weave every JSR-303 annotated method and decorate it with JSR-303 method level validation. By this way we can check pre- and post conditions of method invocation.

The following code will remain:

public void doSomethingWithParam(@NotNull String aParam) {
        int length = aParam.length();
}

In this case, the IllegalArgumentException is thrown by the AOP advice. Nice, isn’t it?

Summary

Together with AOP and JSR-303, defect detection automation can be very powerful. It cleans up the sources and also helps not to violate the DRY principle. Feel free to consult the AspectJ documentation or the JSR-303 reference implementation Hibernate Validator for more information.

Git revision: 2e692ad