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

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


答案 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

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

这给我们留下了

javax.annotation

这也是最短的一个。

只有一种语法会更好:.正如过去其他软件包从到不同方向发展的那样,javax.annotation将是朝着正确方向迈出的一步。java.annotation.Nullablejavaxjava

实现

我希望它们基本上都有相同的琐碎实现,但详细的分析表明这不是真的。

首先是相似之处:

注释都有行@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 样式上下文。下表给出了概述:

方法 参数 LOCAL_VARIABLE
android.support.annotation ✔️ ✔️ ✔️
edu.umd.cs.findbugs.annotations ✔️ ✔️ ✔️ ✔️
org.jetbrains.annotation ✔️ ✔️ ✔️ ✔️
龙目岛 ✔️ ✔️ ✔️ ✔️
javax.validation.constraints ✔️ ✔️ ✔️

org.eclipse.jdt.annotation,并使用JLS 4.11中定义的上下文,在我看来,这是正确的方法。javax.annotationorg.checkerframework.checker.nullness.qual

这给我们留下了

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

在这一轮中。

法典

为了帮助您自己比较更多细节,我在下面列出了每个注释的代码。为了使比较更容易,我删除了注释,导入和注释。(除了Android软件包中的类之外,他们都有)。我对线条和字段进行了重新排序,并对资格进行了规范化。@Documented@Documented@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@NonNulljavax.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();
    }
}

支持

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

总结

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

如果你不打算使用Checker框架,javax.annotationJSR-305)仍然是你目前最好的选择。

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


来源

  • android.support.annotationandroid-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotationsfindbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotationorg.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotationsjetbrains-annotations-13.0.jar
  • javax.annotationgwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qualchecker-framework-2.1.9.zip
  • lombok从提交lombokf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraintsvalidation-api-1.0.0.GA-sources.jar

答案 2

我非常喜欢Checker框架,它是类型注释(JSR-308)的实现,用于实现缺陷检查器,如空性检查器。我还没有真正尝试过任何其他比较,但我对这个实现很满意。

我不隶属于提供该软件的团体,但我是粉丝。

我喜欢这个系统的四件事:

  1. 它有一个用于空性的缺陷检查器(@Nullable),但也有用于不可变性和实习(以及其他)的缺陷检查器。我使用第一个(空性),我试图使用第二个(不可变性/ IGJ)。我正在尝试第三个,但我不确定是否长期使用它。我还不相信其他检查器的一般有用性,但很高兴知道框架本身是一个实现各种附加注释和检查器的系统。

  2. 空性检查的默认设置效果很好:除局部变量 (NNEL) 外的非空值。基本上,这意味着默认情况下,检查器将除局部变量以外的 everyhing(实例变量、方法参数、泛型类型等)视为默认情况下它们具有@NonNull类型。根据文档:

    NNEL 默认值导致代码中的显式批注数量最少。

    如果 NNEL 不适合您,您可以为类或方法设置不同的默认值。

  3. 此框架允许您通过在注释中包含注释来使用 with,而无需创建对框架的依赖关系:例如.这很好,因为您可以注释和检查库或共享代码,但仍然能够在另一个不使用框架的项目中使用该库/共享编码。这是一个不错的功能。我已经习惯了使用它,即使我现在倾向于在所有项目上启用Checker框架。/*@Nullable*/

  4. 该框架有一种方法可以通过使用存根文件对尚未批注为空的 API 进行批注。