我应该使用哪个 @NotNull Java 注释? [英] Which @NotNull Java annotation should I use?

查看:33
本文介绍了我应该使用哪个 @NotNull Java 注释?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望让我的代码更具可读性,并使用 IDE 代码检查和/或静态代码分析(FindBugs 和 Sonar)等工具来避免 NullPointerExceptions.许多工具似乎与彼此的 @NotNull/@NonNull/@Nonnull 注释不兼容,并在我的代码中列出所有这些工具可怕的阅读.任何建议哪个是最好的"?以下是我找到的等效注释列表:

解决方案

自从 JSR 305(其目标是标准化@NonNull@Nullable)已经沉寂了好几年,恐怕也没有好的答案.我们所能做的就是找到一个务实的解决方案,我的解决方案如下:

语法

从纯粹的风格角度来看,我想避免对 IDE、框架或除 Java 本身以外的任何工具包的任何引用.

这排除了:

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

这给我们留下了 javax.validation.constraintsjavax.annotation.前者带有 JEE.如果这比 javax.annotation 更好,后者可能最终与 JSE 一起出现或根本不会出现,这是一个有争议的问题.我个人更喜欢 javax.annotation 因为我不喜欢 JEE 依赖.

这给我们留下了

javax.annotation

这也是最短的.

只有一种语法会更好:java.annotation.Nullable.随着其他包的毕业从过去的 javaxjava,javax.annotation 会朝着正确方向迈出的一步.

实施

我希望它们都有基本相同的琐碎实现,但详细分析表明事实并非如此.

首先是相似之处:

@NonNull 注释都有一行

public @interface NonNull {}

除了

  • org.jetbrains.annotations 调用它 @NotNull 并有一个简单的实现
  • javax.annotation 有更长的实现
  • javax.validation.constraints 也称它为 @NotNull 并有一个实现

@Nullable注解都有一行

public @interface Nullable {}

除了(再次)org.jetbrains.annotations 及其琐碎的实现.

区别:

最引人注目的是

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

都有运行时注释(@Retention(RUNTIME)),而

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

只是编译时间(@Retention(CLASS)).

this SO answer中所述,运行时注释的影响比人们想象的要小,但它们有好处使工具能够进行运行时检查,除了编译时的.

另一个重要的区别是注释可以在代码中的哪里使用.有两种不同的方法.一些包使用 JLS 9.6.4.1 样式上下文.下表提供了概览:

<前>现场方法参数 LOCAL_VARIABLEandroid.support.annotation X X Xedu.umd.cs.findbugs.annotations X X X Xorg.jetbrains.annotation X X X X龙目岛 X X X Xjavax.validation.constraints X X X

org.eclipse.jdt.annotationjavax.annotationorg.checkerframework.checker.nullness.qual 使用在JLS 4.11,在我看来这是正确的做法.

这给我们留下了

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

在这一轮中.

代码

为了帮助您自己比较更多的细节,我在下面列出了每个注释的代码.为了便于比较,我删除了注释、导入和 @Documented 注释.(它们都有 @Documented,除了 Android 包中的类).我重新排列了行和 @Target 字段并标准化了资格.

package android.support.annotation;@保留(类)@目标({字段,方法,参数})公共@interface NonNull {}

<小时>

包edu.umd.cs.findbugs.annotations;@保留(类)@目标({字段,方法,参数,本地变量})公共@interface NonNull {}

<小时>

package org.eclipse.jdt.annotation;@保留(类)@Target({ TYPE_USE })公共@interface NonNull {}

<小时>

package org.jetbrains.annotations;@保留(类)@目标({字段,方法,参数,本地变量})public @interface NotNull {String value() 默认"";}

<小时>

package javax.annotation;@TypeQualifier@保留(运行时间)公共@interface 非空{当 when() 默认 When.ALWAYS;静态类 Checker 实现 TypeQualifierValidator{public 当 forConstantValue(Nonnull qualifierqualifierArgument,对象值) {如果(值 == 空)返回 When.NEVER;返回 When.ALWAYS;}}}

<小时>

package org.checkerframework.checker.nullness.qual;@保留(运行时间)@Target({TYPE_USE, TYPE_PARAMETER})@SubtypeOf(MonotonicNonNull.class)@ImplicitFor(类型 = {TypeKind.PACKAGE,TypeKind.INT,TypeKind.BOOLEAN,TypeKind.CHAR,TypeKind.DOUBLE,TypeKind.FLOAT,类型Kind.LONG,TypeKind.SHORT,类型Kind.BYTE},文字 = {LiteralKind.STRING})@DefaultQualifierInHierarchy@DefaultFor({TypeUseLocation.EXCEPTION_PARAMETER})@DefaultInUncheckedCodeFor({TypeUseLocation.PARAMETER, TypeUseLocation.LOWER_BOUND})公共@interface NonNull {}

<小时>

为了完整起见,这里是 @Nullable 实现:

package android.support.annotation;@保留(类)@Target({方法,参数,字段})公共 @interface 可为空 {}

<小时>

包edu.umd.cs.findbugs.annotations;@目标({字段,方法,参数,本地变量})@保留(类)公共 @interface 可为空 {}

<小时>

package org.eclipse.jdt.annotation;@保留(类)@Target({ TYPE_USE })公共 @interface 可为空 {}

<小时>

package org.jetbrains.annotations;@保留(类)@目标({字段,方法,参数,本地变量})public @interface Nullable {String value() default "";}

<小时>

package javax.annotation;@TypeQualifierNickname@Nonnull(when = When.UNKNOWN)@保留(运行时间)公共 @interface 可为空 {}

<小时>

package org.checkerframework.checker.nullness.qual;@保留(运行时间)@Target({TYPE_USE, TYPE_PARAMETER})@SubtypeOf({})@ImplicitFor(文字 = {LiteralKind.NULL},typeNames = {java.lang.Void.class})@DefaultInUncheckedCodeFor({TypeUseLocation.RETURN, TypeUseLocation.UPPER_BOUND})公共 @interface 可为空 {}

下面两个包没有@Nullable,所以我单独列出来;Lombok 有一个很无聊的@NonNull.在 javax.validation.constraints 中,@NonNull 实际上是一个 @NotNull它的实现时间很长.

包 lombok;@保留(类)@目标({字段,方法,参数,本地变量})公共@interface NonNull {}

<小时>

package javax.validation.constraints;@保留(运行时间)@Target({ 字段,方法,注释类型,构造函数,参数})@Constraint(validatedBy = {})公共@interface NotNull {String message() 默认{javax.validation.constraints.NotNull.message}";Class<?>[] groups() default { };类[] payload() 默认{};@目标({方法,字段,注释类型,构造函数,参数})@保留(运行时间)@记录@接口列表{NotNull[] value();}}

<小时>

支持

根据我的经验,javax.annotation 至少得到 Eclipse 和开箱即用的 Checker 框架的支持.

总结

我理想的注释是带有 Checker 框架实现的 java.annotation 语法.

如果您不打算使用 Checker 框架,javax.annotation (JSR-305) 仍然是您目前最好的选择.

如果您愿意购买 Checker 框架,请使用他们的 org.checkerframework.checker.nullness.qual.

<小时>

来源

  • android.support.annotation 来自 android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations 来自 findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation 来自 org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations 来自 jetbrains-annotations-13.0.jar
  • javax.annotation 来自 gwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qual 来自 checker-framework-2.1.9.zip
  • lombok from lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints 来自 validation-api-1.0.0.GA-sources.jar

I'm looking to make my code more readable as well as use tooling like IDE code inspection and/or static code analysis (FindBugs and Sonar) to avoid NullPointerExceptions. Many of the tools seem incompatible with each others' @NotNull/@NonNull/@Nonnull annotation and listing all of them in my code would be terrible to read. Any suggestions of which one is the 'best'? Here is the list of equivalent annotations I've found:

解决方案

Since JSR 305 (whose goal was to standardize @NonNull and @Nullable) has been dormant for several years, I'm afraid there is no good answer. All we can do is to find a pragmatic solution and mine is as follows:

Syntax

From a purely stylistic standpoint I would like to avoid any reference to IDE, framework or any 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

Which leaves us with either javax.validation.constraints or javax.annotation. The former comes with JEE. If this is better than javax.annotation, which might come eventually with JSE or never at all, is a matter of debate. I personally prefer javax.annotation because I wouldn't like the JEE dependency.

This leaves us with

javax.annotation

which is also the shortest one.

There is only one syntax which would even be better: java.annotation.Nullable. As other packages graduated from javax to java in the past, the javax.annotation would be a step in the right direction.

Implementation

I was hoping that they all have basically the same trivial implementation, but a detailed analysis showed that this is not true.

First for the similarities:

The @NonNull annotations all have the line

public @interface NonNull {}

except for

  • org.jetbrains.annotations which calls it @NotNull and has a trivial implementation
  • javax.annotation which has a longer implementation
  • javax.validation.constraints which also calls it @NotNull and has an implementation

The @Nullableannotations all have the line

public @interface Nullable {}

except for (again) the org.jetbrains.annotations with their trivial implementation.

For the differences:

A striking one 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

are only compile time (@Retention(CLASS)).

As described in this SO answer the impact of runtime annotations is smaller than one might think, but they have the benefit of enabling tools to do runtime checks in addition to the compile time ones.

Another important difference is where in the code the annotations can be used. There are two different approaches. Some packages use JLS 9.6.4.1 style contexts. The following table gives an overview:


                                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   

org.eclipse.jdt.annotation, javax.annotation and org.checkerframework.checker.nullness.qual use the contexts defined in JLS 4.11, which is in my opinion the right way to do it.

This leaves us with

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

in this round.

Code

To help you to compare further details yourself I list the code of every annotation below. To make comparison easier I removed comments, imports and the @Documented annotation. (they all had @Documented except for the classes from the Android package). I reordered the lines and @Target fields and normalized the qualifications.

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


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


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


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


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


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 {}


For completeness, here are the @Nullable implementations:

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


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


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


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


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


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 {}

The following two packages have no @Nullable, so I list them separately; Lombok has a pretty boring @NonNull. In javax.validation.constraints the @NonNull is actually a @NotNull and it has a longish implementation.

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


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


Support

From my experience, javax.annotation is at least supported by Eclipse and the Checker Framework out of the box.

Summary

My ideal annotation would be the java.annotation syntax with the Checker Framework implementation.

If you don't intend to use the Checker Framework the javax.annotation (JSR-305) is still your best bet for the time being.

If you are willing to buy into the Checker Framework just use their org.checkerframework.checker.nullness.qual.


Sources

  • 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.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations from jetbrains-annotations-13.0.jar
  • javax.annotation from gwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qual from checker-framework-2.1.9.zip
  • lombok from lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints from validation-api-1.0.0.GA-sources.jar

这篇关于我应该使用哪个 @NotNull Java 注释?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆