Which @notnull Java annotation should I use? | the Java Debug notes

This article is participating in “Java Theme Month – Java Debug Notes Event”, see < Event link > for more details.

Lift to ask:

I want to make my code more readable and use tools such as IDE code checking and/or static code analysis to avoid NullPointerExceptions. Many tools seem incompatible with each other, and I list @notnull / @nonNULL / @nonNULL annotations in my code, all of which are hard to read. Do you have the best suggestions? Here’s a list of the equivalent comments I found:

  • Javax.mail. Validation. Constraints. NotNull created for runtime validation, rather than a static analysis. Annotation document

  • Edu. Umd. Cs. Findbugs. Annotations. NonNull by findbugs (dead project) and his derived class SpotBugs static analysis and Sonar (now called Sonarqube) use

    FindBugs annotation document, SpotBugs annotation document

  • Javax.annotation.Nonnull This might also apply to FindBugs, but JSR-305 is inactive. (See also: What is the status of JSR 305?) The source code

  • 1 org. Jetbrains. Annotations NotNull by IntelliJ IDEA IDE for static analysis. Annotation document

  • Lombok.NonNull is used in Project Lombok to control code generation. Placeholder comments because there are no standards. Source code, comment documentation

  • Android. Support. The annotation. NonNull in android available marker annotations, provided by the support – annotations package notes document

  • Org. The eclipse JDT. The annotation. NonNull eclipse for static code analysis Annotation document

Answer:

Since JSR 305 (whose goal is to standardize @nonNULL and @Nullable) has been dormant for a few years, I’m afraid there’s no good answer. All we can do is find a pragmatic solution, and my approach is as follows:

syntactic

From a pure style standpoint, I want to avoid referring to any IDE, framework, or toolkit except Java itself.

This rules out:

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations
  • org.checkerframework.checker.nullness.qual
  • lombok.NonNull

The rest is javax.mail. Validation. Constraints or javax.mail. The annotation. The former comes with JEE. If this is better than Javax.annotation, it may end up with JSE, or notat all, and that’s a moot point. I personally prefer Javax. annotations because I don’t like JEE dependencies.

That leaves us

javax.annotation

This is also the shortest.

There’s only one syntax that’s better: java.annotation.nullable. With other packages upgrading from Javax to Java in the past, Javax.Annotation will be a step in the right direction.

perform

I wish they all had basically the same trivial implementation, but careful analysis shows that this is not the case.

First, the similarities:

The @nonNULL annotation has one line:

public @interface NonNull {}
Copy the code

In addition:

  • Org. Jetbrains. Annotations called @ NotNull and has a simple implementation

  • Javax.annotation with a longer implementation

  • Javax.mail. Validation. Constraints is also known as @ NotNull with implementation

    There’s a line here at @nullableanannotations

public @interface Nullable{}
Copy the code

Again, in addition to the org. Jetbrains. Annotations as well as some of their implementation.

For differences:

What’s striking is that

  • javax.annotation
  • javax.validation.constraints
  • org.checkerframework.checker.nullness.qual

All have RUNTIME annotations (@Retention(RUNTIME)). while

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations

Only in compile phase (@Retention(CLASS)).

As stated in this answer, run-time annotations have less impact than one might think, but their benefit is that they enable tools to perform run-time checks in addition to compile time.

Another important difference is where comments can be used in your code. There are two different approaches. Some packages use the JLS 9.6.4.1 style context. The following table summarizes:

                                FIELD   METHOD  PARAMETER LOCAL_VARIABLE 
android.support.annotation      X       X       X   
edu.umd.cs.findbugs.annotations X       X       X         X
org.jetbrains.annotation        X       X       X         X
lombok                          X       X       X         X
javax.validation.constraints    X       X       X   
Copy the code

Org. The eclipse JDT. The annotation, javax.mail. The annotation and org. Checkerframework. The checker. Nullness. Qua use JLS 4.11 defined in the context, I think this is the right way.

That leaves us

  • javax.annotation
  • org.checkerframework.checker.nullness.qual

Code sample

To help you compare more details for yourself, I’ve listed the code for each comment below. To make it easier, I’ve removed the annotation, import, and @documented. (All files except the classes in the Android package have @Documented.) I reordered and normalized these lines of code and the @target field.

package android.support.annotation;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER})
public @interface NonNull {}
Copy the code

package edu.umd.cs.findbugs.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}
Copy the code

package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface NonNull {}
Copy the code

package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NotNull {String value(a) default ""; }Copy the code

package javax.annotation;
@TypeQualifier
@Retention(RUNTIME)
public @interface Nonnull {
    When when(a) default When.ALWAYS;
    static class Checker implements TypeQualifierValidator<Nonnull> {
        public When forConstantValue(Nonnull qualifierqualifierArgument, Object value) {
            if (value == null)
                return When.NEVER;
            returnWhen.ALWAYS; }}}Copy the code

package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf(MonotonicNonNull.class)
@ImplicitFor( types = { TypeKind.PACKAGE, TypeKind.INT, TypeKind.BOOLEAN, TypeKind.CHAR, TypeKind.DOUBLE, TypeKind.FLOAT, TypeKind.LONG, TypeKind.SHORT, TypeKind.BYTE }, literals = {LiteralKind.STRING} )
@DefaultQualifierInHierarchy
@DefaultFor({TypeUseLocation.EXCEPTION_PARAMETER})
@DefaultInUncheckedCodeFor({TypeUseLocation.PARAMETER, TypeUseLocation.LOWER_BOUND})
public @interface NonNull {}
Copy the code

For completeness, here’s the @nullable implementation:

package android.support.annotation;
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD})
public @interface Nullable {}
Copy the code

package edu.umd.cs.findbugs.annotations;
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
@Retention(CLASS)
public @interface Nullable {}
Copy the code

package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface Nullable {}
Copy the code

package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface Nullable {String value(a) default ""; }Copy the code

package javax.annotation;
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
@Retention(RUNTIME)
public @interface Nullable {}
Copy the code

package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf({})
@ImplicitFor( literals = {LiteralKind.NULL}, typeNames = {java.lang.Void.class} )
@DefaultInUncheckedCodeFor({TypeUseLocation.RETURN, TypeUseLocation.UPPER_BOUND})
public @interface Nullable {}
Copy the code

The following two packages don’t have @Nullable, so I’ll list them separately; Lombox has a little-used @nonnul. In the javax.mail. Validation. Constraints, @ NonNull is actually a @ NotNull, and perform time-consuming very long.

package lombok;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}
Copy the code

package javax.validation.constraints;
@Retention(RUNTIME)
@Target({ FIELD, METHOD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Constraint(validatedBy = {})
public @interface NotNull {
    String message(a) default "{javax.validation.constraints.NotNull.message}"; Class
      [] groups() default { }; Class
      [] payload() default {}; @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @Documented @interface List { NotNull[] value(); }}Copy the code

support

In my experience, Eclipse and the Checker Framework support Javax.Annotation at least out of the box.

conclusion

My ideal annotation is the Java.annotation syntax implemented by the Checker Framework.

If you’re not going to use the Checker Framework, it’s best to use Javax.Annotation (JSR-305) for now.

If you are willing to try the Checker Framework, please use the org. Checkerframework. The Checker. Nullness. Qual.

Source code used

  • android.support.annotation from Android - 5.1.1 _r1. Jar
  • edu.umd.cs.findbugs.annotations from Findbugs annotations - 1.0.0. Jar
  • org.eclipse.jdt.annotation from Org. The eclipse JDT. Annotation_2. 1.0 v20160418-1457. The jar
  • org.jetbrains.annotations from Jetbrains annotations - 13.0. The jar
  • javax.annotation from GWT - dev - 2.5.1 - sources. The jar
  • org.checkerframework.checker.nullness.qual from The checker - framework - 2.1.9. Zip
  • lombok from lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints from Validation - API - 1.0.0. GA - sources. The jar

The original link