我应该使用哪个 @NotNull Java 注释?

新手上路,请多包涵

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

  • javax.validation.constraints.NotNull

创建用于运行时验证,而不是静态分析。

文件

  • edu.umd.cs.findbugs.annotations.NonNull

FindBugs已死项目)及其后继 SpotBugs 静态分析以及因此 Sonar(现为 Sonarqube )使用

FindBugs 文档, SpotBugs 文档

  • javax.annotation.Nonnull

这可能也适用于 FindBugs,但 JSR-305 是不活动的。 (另请参阅: JSR 305 的状态是什么? 来源

  • org.jetbrains.annotations.NotNull

由 IntelliJ IDEA IDE 用于静态分析。

文件

  • lombok.NonNull

用于控制 Project Lombok 中的代码生成。

占位符注释,因为没有标准。

文件

  • androidx.annotation.NonNull

Android 中可用的标记注解,由注解包提供

文件

  • org.eclipse.jdt.annotation.NonNull

由 Eclipse 用于静态代码分析

文件

原文由 jaxzin 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
1 个回答

由于 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 。随着其他软件包在过去从 javax 升级到 java ,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) )。

本 SO 回答 中所述,运行时注释的影响比人们想象的要小,但它们的好处是使工具能够在编译时检查之外进行运行时检查。

另一个重要的区别是可以在代码中的什么 地方 使用注释。有两种不同的方法。一些包使用 JLS 9.6.4.1 样式上下文。下表给出了一个概览:

包裹场地方法范围本地变量android.support.annotation ✔️ ✔️ ✔️ edu.umd.cs.findbugs.注释✔️ ✔️ ✔️ ✔️ org.jetbrains.注释✔️ ✔️ ✔️ ✔️龙目岛✔️ ✔️ ✔️ ✔️ javax.validation.constraints ✔️ ✔️ ✔️

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


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

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

下面两个包没有 @Nullable ,所以单独列出来;龙目岛有一个很无聊的 @NonNull 。在 javax.validation.constraints @NonNull 实际上是一个 @NotNull 它有一个较长的实现。

 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();
    }
}


支持

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

概括

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

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

如果您愿意购买 Checker Framework,只需使用他们的 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 来自 lombok 提交 f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints 来自 validation-api-1.0.0.GA-sources.jar

原文由 Ludwig Weinzierl 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题