【问题标题】:Which @NotNull Java annotation should I use?我应该使用哪个 @NotNull Java 注释?
【发布时间】:2011-06-25 04:47:18
【问题描述】:

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

【问题讨论】:

  • apache 应该发明一个“通用”注释和一个可以将其转换为任何其他注释的工具。解决标准过多问题的方法是发明一个新标准。
  • @irreputable 如果 apache 发明了一个新的“common”,它将有 56 个版本,与其他项目重叠。而且,无论如何它都不是标准的(标准!=普遍)。最好使用任何真正标准的东西, javax?.* 。顺便说一句,这些示例中没有“太多标准”,我只看到 1 或 2。
  • javax.annotation.Nonnull 确实适用于 findbugs(刚刚测试过),这是我使用它的一个令人信服的理由。
  • 如果我简单地写@NotNull,它指的是com.sun.istack.internal.NotNull。天哪...
  • @MozartBrocchini - 在您以前可能使用过 NullObjects 的情况下,选项很有用。但是,它们并没有真正解决与运行时 \@NotNull 注释相同的目标,并且它们引入了繁琐的展开。

标签: java nullpointerexception null annotations ide


【解决方案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。随着其他包裹毕业 过去从javaxjava,javax.annotation 会 朝着正确的方向迈出一步。

实施

我希望它们都有基本相同的简单实现, 但详细分析表明并非如此。

首先是相似之处:

@NonNull注解都有一行

public @interface NonNull {}

除了

  • org.jetbrains.annotations 将其称为 @NotNull 并有一个简单的实现
  • javax.annotation 实现时间较长
  • javax.validation.constraints 也称为 @NotNull 并有一个实现

@Nullableannotations 都有一行

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_VARIABLE android.support.annotation X X X edu.umd.cs.findbugs.annotations X X X X org.jetbrains.annotation X X X X 龙目岛 X X X X javax.validation.constraints X X X

org.eclipse.jdt.annotationjavax.annotationorg.checkerframework.checker.nullness.qual 使用定义在 JLS 4.11,我认为这是正确的做法。

这给我们留下了

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

在这一轮中。

代码

为了帮助您自己比较更多细节,我在下面列出了每个注释的代码。 为了便于比较,我删除了 cmets、imports 和 @Documented 注释。 (除了 Android 包中的类之外,它们都有@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,所以我单独列出来;龙目岛有一个很无聊的@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 的支持。

总结

我理想的注释应该是带有 Checker Framework 实现的 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 来自 lombok 提交 f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints 来自validation-api-1.0.0.GA-sources.jar

【讨论】:

  • javax.annotation 的缺点是它 a) 基于死的 JSR,b) 很难找到只提供注释并进行维护的工件。来自 findbugs 的不是:search.maven.org/…
  • 反对 javax.annotation 的另一点是它会导致 Java 9 出现问题,因为其他模块也在该包中提供类 (jax-ws)。
  • @kevinarpe:Findbugs 项目已死,后续项目 Spotbugs 正在删除这些注释:github.com/spotbugs/spotbugs/pull/180
  • JSR 305,本来可以标准化 javax.annotation.NonNull,但由于其规范负责人擅离职守,因此从未完成。这与 Oracle 的任何决定无关。
  • 另一个不使用 jsr305.jar 的原因是它明显违反了 Oracle Java 二进制许可:github.com/google/guava/issues/2960
【解决方案2】:

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

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

我喜欢这个系统的四点:

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

  2. default setting for nullness checking 运行良好:非空,除了本地人 (NNEL)。基本上这意味着默认情况下,检查器将除局部变量之外的所有内容(实例变量、方法参数、泛型类型等)视为默认情况下它们具有 @NonNull 类型。根据文档:

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

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

  3. 此框架允许您通过将注释包含在注释中来与 without creating a dependency on the framework 一起使用:例如/*@Nullable*/。这很好,因为您可以注释和检查库或共享代码,但仍然能够在另一个不使用框架的项目中使用该库/共享编码。这是一个不错的功能。我已经习惯了使用它,尽管我现在倾向于在我的所有项目中启用 Checker 框架。

  4. 该框架有一种方法可以annotate APIs 您使用的尚未通过使用存根文件进行空值注释。

【讨论】:

  • 看起来很棒,我想使用它,但不能。为什么选择 GPL?难道不是 LGPL 吗?
  • 根据FAQ:“更宽松的 MIT 许可证适用于您可能希望包含在自己的程序中的代码,例如注释。”
  • 链接当前已断开。但是对于使用 Checker Framework 的建议 +1。
  • 很遗憾在最新版本中删除了不变性检查器。
  • Checker Framework 也建议在Oracle Java Tutorials 中。
【解决方案3】:

我使用 IntelliJ,因为我最关心的是 IntelliJ 标记可能产生 NPE 的事物。我同意在 JDK 中没有标准注释令人沮丧。有人说要添加它,它可能会进入 Java 7。在这种情况下,会有更多的选择!

【讨论】:

  • 更新:IntelliJ 现在支持以上所有注解进行代码高亮,因此您不再局限于 IntelliJ 的注解:blogs.jetbrains.com/idea/2011/03/…
  • javax.annotation.Nonnull 被更广泛地接受了,不是吗?
  • @DanielAlexiuc 但不幸的是,它并没有将它们用于运行时检查,因此使用 JetBrains 仍然有好处...
  • @Trejkaz 自 2016.3 以来,它为所有这些创建运行时检查。
【解决方案4】:

根据Java 7 features list,JSR-308 类型注释被推迟到 Java 8。甚至没有提到 JSR-305 注释。

在最新的 JSR-308 草案的 appendix 中有一些关于 JSR-305 状态的信息。这包括观察到 JSR-305 注释似乎已被放弃。 JSR-305 页面也将其显示为“非活动”。

同时,务实的答案是使用最广泛使用的工具支持的注释类型......并准备好在情况发生变化时更改它们。


事实上,JSR-308 并没有定义任何注解类型/类,看起来他们认为它超出了范围。 (考虑到 JSR-305 的存在,他们是对的)。

但是,如果 JSR-308 真的看起来像将其纳入 Java 8,那么如果对 JSR-305 的兴趣恢复,我也不会感到惊讶。 AFAIK,JSR-305 团队还没有正式放弃他们的工作。他们只是安静了 2 年多。

有趣的是,Bill Pugh(JSR-305 的技术负责人)是 FindBugs 背后的人之一。

【讨论】:

【解决方案5】:

对于 Android 项目,您应该使用 android.support.annotation.NonNullandroid.support.annotation.NullableSupport Library 中提供了这些和其他有用的 Android 特定注释。

来自http://tools.android.com/tech-docs/support-annotations

支持库本身也带有这些注释 注释,因此作为支持库的用户,Android Studio 将 已经检查您的代码并根据这些标记潜在问题 注释。

【讨论】:

  • 为该建议提供理由会很有用。
  • tools.android.com/tech-docs/support-annotations "支持库本身也已经用这些注解进行了注解,因此作为支持库的用户,Android Studio 已经会检查您的代码并根据这些注解标记潜在的问题。"
  • BTW Android Studio 也支持带有javax.annotation.* 注释的jsr305
【解决方案6】:

如果有人只是在寻找 IntelliJ 类:您可以从 maven 存储库中获取它们

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 

【讨论】:

  • 这是导致 Intellij 发出警告的原因,是的。
  • 当前版本(截至 2017 年 5 月)为 15.0
  • 您的权利。我已经更新了版本。即使我猜它并没有太大变化。
  • 请记住,JetBrains 注释不会为运行时保留,因此 Guice @Nullable 支持例如不适用于它。
【解决方案7】:

区分静态分析和运行时分析。对内部内容使用静态分析,对代码的公共边界使用运行时分析。

对于不应该为空的东西:

  • 运行时检查:使用“if (x == null) ...”(零依赖)或@javax.validation.NotNull(带有bean验证)或@lombok.NonNull(简单明了)或番石榴Preconditions.checkNotNull (...)

    • 对方法返回类型使用 Optional(仅)。 Java8 或 Guava。
  • 静态检查:使用@NonNull 注释

  • 在适合的地方,在类或包级别使用@...NonnullByDefault 注释。自己创建这些注释(示例很容易找到)。
    • 否则,在方法返回时使用 @...CheckForNull 以避免 NPE

这应该会产生最好的结果:IDE 中的警告、Findbugs 和 checkerframework 的错误、有意义的运行时异常。

不要指望静态检查会成熟,它们的命名不规范,不同的库和 IDE 对待它们的方式不同,忽略它们。 JSR305 javax.annotations.* 类看起来像标准的,但实际上并非如此,它们会导致 Java9+ 拆分包。

一些注释说明:

  • 带有 javax.validation.* 包的 Findbugs/spotbugs/jsr305 注释与 Java9+ 中的其他模块冲突,也可能违反 Oracle 许可
  • Spotbugs 注释在编译时仍然依赖于 jsr305/findbugs 注释(在编写 https://github.com/spotbugs/spotbugs/issues/421 时)
  • jetbrains @NotNull 名称与 @javax.validation.NotNull 冲突。
  • 用于静态检查的 jetbrains、eclipse 或 checkersframework 注释优于 javax.annotations 的优势在于它们不会与 Java9 及更高版本中的其他模块发生冲突
  • @javax.annotations.Nullable 对 Findbugs/Spotbugs 并不意味着您(或您的 IDE)认为它意味着什么。 Findbugs 将忽略它(在成员上)。悲伤,但真实 (https://sourceforge.net/p/findbugs/bugs/1181)
  • 对于 IDE 外部的静态检查,有 2 个免费工具:Spotbugs(以前称为 Findbugs)和 checkersframework。
  • Eclipse 库有@NonNullByDefault,jsr305 只有@ParametersAreNonnullByDefault。这些只是将基本注释应用于包(或类)中的所有内容的便捷包装器,您可以轻松创建自己的。这可以在包装上使用。这可能与生成的代码(例如 lombok)冲突。
  • 对于您与其他人共享的库,应避免使用 lombok 作为导出依赖项,传递依赖项越少越好
  • 使用 Bean 验证框架功能强大,但需要很高的开销,因此为了避免手动检查 null 就显得过大了。
  • 对字段和方法参数使用 Optional 是有争议的(您可以轻松找到有关它的文章)
  • Android 空注释是 Android 支持库的一部分,它们带有很多其他类,不能很好地与其他注释/工具配合使用

在 Java9 之前,这是我的建议:

// file: package-info.java
@javax.annotation.ParametersAreNonnullByDefault
package example;


// file: PublicApi
package example;

public interface PublicApi {

    Person createPerson(
        // NonNull by default due to package-info.java above
        String firstname,
        String lastname);
}

// file: PublicApiImpl
public class PublicApiImpl implements PublicApi {
    public Person createPerson(
            // In Impl, handle cases where library users still pass null
            @Nullable String firstname, // Users  might send null
            @Nullable String lastname // Users might send null
            ) {
        if (firstname == null) throw new IllagalArgumentException(...);
        if (lastname == null) throw new IllagalArgumentException(...);
        return doCreatePerson(fistname, lastname, nickname);
    }

    @NonNull // Spotbugs checks that method cannot return null
    private Person doCreatePerson(
             String firstname, // Spotbugs checks null cannot be passed, because package has ParametersAreNonnullByDefault
             String lastname,
             @Nullable String nickname // tell Spotbugs null is ok
             ) {
         return new Person(firstname, lastname, nickname);
    }

    @CheckForNull // Do not use @Nullable here, Spotbugs will ignore it, though IDEs respect it
    private Person getNickname(
         String firstname,
         String lastname) {
         return NICKNAMES.get(firstname + ':' + lastname);
    }
}

请注意,当取消引用可为空的方法参数时,无法使 Spotbugs 发出警告(在撰写本文时,Spotbugs 版本为 3.1)。也许 checkerframework 可以做到这一点。

遗憾的是,这些注释没有区分具有任意调用点的库的公共方法和每个调用点都可以知道的非公共方法的情况。因此,在单个声明中不可能实现“指示不希望使用 null,但仍准备传递 null”的双重含义,因此上面的示例对接口和实现有不同的注释。

对于拆分接口方法不实用的情况,以下方法是一种折衷方案:

        public Person createPerson(
                @NonNull String firstname,
                @NonNull String lastname
                ) {
            // even though parameters annotated as NonNull, library clients might call with null.
            if (firstname == null) throw new IllagalArgumentException(...);
            if (lastname == null) throw new IllagalArgumentException(...);
            return doCreatePerson(fistname, lastname, nickname);
        }

这有助于客户端不传递 null(编写正确的代码),同时返回有用的错误。

【讨论】:

  • 我现在才找到这个答案,但是@tkruse,你在哪里找到这个:“Eclipse jdt 注释不适用于静态方法返回和其他一些情况”? (第一部分不是真的,第二部分很模糊:))。
  • @StephanHerrmann:我不记得了。我删除了要点。
【解决方案8】:

JSR305 和 FindBugs 是由同一个人创作的。两者都维护得很差,但都是标准的,并且受到所有主要 IDE 的支持。好消息是它们按原样运行良好。

这里是默认情况下如何将@Nonnull 应用于所有类、方法和字段。 见https://stackoverflow.com/a/13319541/14731https://stackoverflow.com/a/9256595/14731

  1. 定义@NotNullByDefault
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;


    /**
     * This annotation can be applied to a package, class or method to indicate that the class fields,
     * method return types and parameters in that element are not null by default unless there is: <ul>
     * <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
     * case the annotation of the corresponding parameter in the superclass applies) <li> there is a
     * default parameter annotation applied to a more tightly nested element. </ul>
     * <p/>
     * @see https://stackoverflow.com/a/9256595/14731
     */
    @Documented
    @Nonnull
    @TypeQualifierDefault(
    {
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.FIELD,
        ElementType.LOCAL_VARIABLE,
        ElementType.METHOD,
        ElementType.PACKAGE,
        ElementType.PARAMETER,
        ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NotNullByDefault
    {
    }

2。给每个包添加注解:package-info.java

@NotNullByDefault
package com.example.foo;

更新:截至 2012 年 12 月 12 日,JSR 305 被列为“休眠”。根据文档:

被执行委员会投票为“休眠”的 JSR,或已达到其自然寿命终点的 JSR。

看起来JSR 308 正在进入 JDK 8,虽然 JSR 没有定义 @NotNull,但随附的 Checkers Framework 确实如此。在撰写本文时,由于这个错误,Maven 插件无法使用:https://github.com/typetools/checker-framework/issues/183

【讨论】:

  • 修复了 maven 的 showstopper 问题。所以这应该再次成为一个选项。
  • 我通过 Maven 使用 FindBugs,我的 IDE 没有做任何事情,这避免了 IDE 特定的注释,你会推荐什么?
  • @ChristopheRoussy 您的问题是特定于 IDE 的。请打开一个单独的问题。
【解决方案9】:

Eclipse 也有自己的注解。

org.eclipse.jdt.annotation.NonNull

详情请见http://wiki.eclipse.org/JDT_Core/Null_Analysis

【讨论】:

  • 看起来这将从 Eclipse 3.8 (Juno) 集成,这将使 Eclipse 在这方面与 IntelliJ 保持一致。它还应该允许您配置自己的 Null 注释(例如 javax.annotation.Nonnull),并且可以选择将 NotNull 设为默认值。
【解决方案10】:

只是指出 Java Validation API (javax.validation.constraints.*) 不附带 @Nullable 注释,这在静态分析上下文中非常有价值。这对于运行时 bean 验证是有意义的,因为这是 Java 中任何非原始字段的默认值(即无需验证/强制执行)。出于所述目的,应权衡替代方案。

【讨论】:

    【解决方案11】:

    很遗憾,JSR 308 不会在此处添加比此项目本地 Not Null 建议更多的值

    Java 8 不会附带一个默认注释或它自己的Checker 框架。 与 Find-bugs 或 JSR 305 类似,这个 JSR 由一小群主要是学术团队维护得很差。

    它背后没有商业力量,因此JSR 308 现在推出EDR 3JCP 的早期草案审查),而Java 8 应该在不到 6 个月内发货:-O 类似于310 顺便说一句。但与 308 Oracle 不同的是,它现在从其创始人手中接管了这件事,以尽量减少对 Java 平台造成的损害。

    每个项目、供应商和学术课程(例如 Checker FrameworkJSR 308 背后的那些)都将创建自己的专有检查器注释。

    在未来几年内使源代码不兼容,直到找到一些流行的妥协方案并可能添加到Java 910,或通过Apache CommonsGoogle Guava 等框架;-)

    【讨论】:

      【解决方案12】:

      安卓

      这个答案是特定于 Android 的。 Android 有一个名为support-annotations 的支持包。这提供了dozensAndroid specific 注释,还提供了common ones,如NonNullNullable 等。

      要添加 support-annotations 包,请在 build.gradle 中添加以下依赖项:

      compile 'com.android.support:support-annotations:23.1.1'
      

      然后使用:

      import android.support.annotation.NonNull;
      
      void foobar(@NonNull Foo bar) {}
      

      【讨论】:

        【解决方案13】:

        在等待上游整理(Java 8?)的同时,您还可以定义自己的项目本地 @NotNull@Nullable 注释。这在您使用 Java SE 的情况下也很有用,默认情况下是 javax.validation.constraints isn't available

        import java.lang.annotation.*;
        
        /**
         * Designates that a field, return value, argument, or variable is
         * guaranteed to be non-null.
         */
        @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
        @Documented
        @Retention(RetentionPolicy.CLASS)
        public @interface NotNull {}
        
        /**
         * Designates that a field, return value, argument, or variable may be null.
         */
        @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
        @Documented
        @Retention(RetentionPolicy.CLASS)
        public @interface Nullable {}
        

        诚然,这主要是出于装饰或面向未来的目的,因为上述内容显然本身并没有为这些注释的静态分析添加任何支持。

        【讨论】:

          【解决方案14】:

          这里的答案已经太多了,但是 (a) 现在是 2019 年,仍然没有“标准”Nullable 和 (b) 没有其他答案引用 Kotlin。

          对 Kotlin 的引用很重要,因为 Kotlin 与 Java 100% 可互操作,并且它具有核心的 Null 安全特性。在调用 Java 库时,它可以利用这些注解让 Kotlin 工具知道 Java API 是否可以接受或返回null

          据我所知,唯一与 Kotlin 兼容的 Nullable 包是 org.jetbrains.annotationsandroid.support.annotation(现在是 androidx.annotation)。后者仅与 Android 兼容,因此不能在非 Android JVM/Java/Kotlin 项目中使用。但是,JetBrains 包在任何地方都可以使用。

          因此,如果您开发的 Java 包也应该在 Android 和 Kotlin 中运行(并受 Android Studio 和 IntelliJ 支持),那么您的最佳选择可能是 JetBrains 包。

          马文:

          <dependency>
              <groupId>org.jetbrains</groupId>
              <artifactId>annotations-java5</artifactId>
              <version>15.0</version>
          </dependency>
          

          分级:

          implementation 'org.jetbrains:annotations-java5:15.0'
          

          【讨论】:

          【解决方案15】:

          在 Java 8 中还有另一种方法可以做到这一点。 我正在做两件事来完成我需要的事情:

          1. 通过使用java.util.Optional 包装可空字段,使可空字段与类型显式化
          2. 使用java.util.Objects.requireNonNull 在构造时检查所有不可为空的字段是否不为空

          例子:

          编辑:忽略第一个示例,我只是将这里作为 cmets 对话的上下文。在此之后跳到推荐的选项(第二个代码块)。

              import static java.util.Objects.requireNonNull;
          
              public class Role {
          
                private final UUID guid;
                private final String domain;
                private final String name;
                private final Optional<String> description;
          
                public Role(UUID guid, String domain, String name, Optional<String> description) {
                  this.guid = requireNonNull(guid);
                  this.domain = requireNonNull(domain);
                  this.name = requireNonNull(name);
                  this.description = requireNonNull(description);
                }
             }
          

          所以我的问题是,我们在使用 java 8 时甚至需要注释吗?

          编辑:我后来发现有些人认为在参数中使用Optional 是一种不好的做法,这里有一个很好的讨论,有优缺点Why should Java 8's Optional not be used in arguments

          推荐选项,因为在参数中使用 Optional 不是最佳实践,我们需要 2 个构造函数:

          public class Role {
                
                // Required fields, will not be null (unless using reflection) 
                private final UUID guid;
                private final String domain;
                private final String name;
                // Optional field, not null but can be empty
                private final Optional<String> description;
          
            //Non null description
            public Role(UUID guid, String domain, String name, String description) {
                  this.guid = requireNonNull(guid);
                  this.domain = requireNonNull(domain);
                  this.name = requireNonNull(name);
          
                  // description will never be null
                  requireNonNull(description);
          
                  // but wrapped with an Optional
                  this.description = Optional.of(description);
                }
          
            // Null description is assigned to Optional.empty
            public Role(UUID guid, String domain, String name) {
                  this.guid = requireNonNull(guid);
                  this.domain = requireNonNull(domain);
                  this.name = requireNonNull(name);
                  this.description = Optional.empty();
                }
            //Note that this accessor is not a getter.
            //I decided not to use the "get" suffix to distinguish from "normal" getters 
            public Optional<String> description(){ return description;} 
           }
          

          【讨论】:

          • 我想说你仍然需要所有 4 个形式参数的 @NotNull 注释,以便静态分析检查器知道你的意图,即没有一个应该为空。 Java 语言中还没有任何东西可以强制执行这一点。如果您是防御性编程,您还应该检查描述是否不为空。
          • 我仍然可以编写此代码:new Role(null,null,null,null);。使用注释,我的 IDE 和静态分析将警告不能将 null 传递给这些参数。没有它,我直到我运行代码才发现。这就是注解的价值。
          • 我也在开发人员可以使用他们喜欢的任何 IDE 或文本编辑器的环境中,它不是相互排斥的。然后,我们还将 maven-pmd-plugin 和/或 SonarQube 集成到构建过程中,以鼓励和突出,甚至在合并前控制代码质量问题,例如在拉取请求中。
          • 可选并不意味着用作方法参数或私有字段。例如:stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
          • @assylias 是的,后来我发现了,他们说不推荐,因为它不会给我们买任何东西,我绝对能理解他们的理性。在我放在这里的这种情况下,可以使参数description 不为空,并且客户端代码可以传递一个空字符串,但在许多情况下,区分空字符串和没有值可能很方便。谢谢你的评论。我会更新答案。
          【解决方案16】:

          如果您正在为 android 开发,那么您在某种程度上与 Eclipse 相关联(编辑:在撰写本文时,不再),它有自己的注释。它包含在 Eclipse 3.8+ (Juno) 中,但默认禁用。

          您可以在 Preferences > Java > Compiler > Errors/Warnings > Null analysis(可折叠部分在底部)启用它。

          勾选“启用基于注释的空值分析”

          http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage 有设置建议。但是,如果您的工作区中有外部项目(例如 facebook SDK),它们可能无法满足这些建议,并且您可能不想在每次 SDK 更新时修复它们;-)

          我用:

          1. 空指针访问:错误
          2. 违反空规范:错误(链接到点 #1)
          3. 潜在的空指针访问:警告(否则 facebook SDK 会出现警告)
          4. null 注释和 null 推断之间的冲突:警告(链接到第 3 点)

          【讨论】:

          • 绑定到 Eclipse? 不正确。
          • @DavidCowden IntelliJ IDEA 支持 Android 开发,我认为,在 AndroidStudio 推出之前的一段时间就可以使用。
          • @MārtiņšBriedis 是的,这是真的。我想你的意思是@chaqke
          • 值得注意的是,android 和 intellij 有单独的注解,并且可能会一直保持这种状态,直到 java 包含官方注解。这些是在 Eclipse 中使用 Eclipse 注释的说明。
          • 它从未与 Eclipse 绑定。你可以使用任何你想要的 IDE。
          【解决方案17】:

          如果您使用 Spring Framework 构建应用程序,我建议您使用来自 Beans Validation javax.validation.constraints.NotNull 打包在以下依赖项中:

              <dependency>
                  <groupId>javax.validation</groupId>
                  <artifactId>validation-api</artifactId>
                  <version>1.1.0.Final</version>
              </dependency>
          

          这个注解的主要优点是 Spring 提供了对方法参数和使用javax.validation.constraints.NotNull 注解的类字段的支持。启用支持所需要做的就是:

          1. 提供 api jar 用于 bean 验证和 jar 实现 jsr-303/jsr-349 注释的验证器(它带有 Hibernate Validator 5.x 依赖项):

            <dependency>
                <groupId>javax.validation</groupId>
                <artifactId>validation-api</artifactId>
                <version>1.1.0.Final</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>5.4.1.Final</version>
            </dependency>
            
          2. 将 MethodValidationPostProcessor 提供给 spring 的上下文

              @Configuration
              @ValidationConfig
              public class ValidationConfig implements MyService {
            
                    @Bean
                    public MethodValidationPostProcessor providePostProcessor() {
                          return new MethodValidationPostProcessor()
                    }
              }
            
          3. 最后你用 Spring 的 org.springframework.validation.annotation.Validated 注释你的类,验证将由 Spring 自动处理。

          例子:

          @Service
          @Validated
          public class MyServiceImpl implements MyService {
          
            @Override
            public Something doSomething(@NotNull String myParameter) {
                  // No need to do something like assert myParameter != null  
            }
          }
          

          当您尝试调用方法 doSomething 并将 null 作为参数值传递时,spring(通过 HibernateValidator)将抛出 ConstraintViolationException。此处无需手动操作。

          您还可以验证返回值。

          javax.validation.constraints.NotNull 用于 Beans Validation Framework 的另一个重要好处是,目前它仍在开发中,并且计划在新版本 2.0 中添加新功能。

          @Nullable 呢?在 Beans Validation 1.1 中没有类似的东西。好吧,我可以争辩说,如果你决定使用@NotNull,那么所有没有用@NonNull 注释的东西实际上都是“可空的”,所以@Nullable 注释是没用的。

          【讨论】:

          • 请不要使用。它用于运行时验证,而不是静态代码分析。有关详细信息,请参阅justsomejavaguy.blogspot.com/2011/08/…。资料来源:@luis.espinal 获得 219 票的已删​​除答案。
          • @koppor:我不同意。如果这不是用于使用的,为什么 Spring 会在运行时处理它。此外,Beans 验证框架允许创建纯粹用于运行时分析的注释,因为它允许在运行时访问 Context 对象(当前已注释/验证的实例)。
          【解决方案18】:

          如果您正在处理一个大型项目,最好创建您自己的 @Nullable 和/或@NotNull 注释。

          例如:

          @java.lang.annotation.Documented
          @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)
          @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD,
                                        java.lang.annotation.ElementType.METHOD,    
                                        java.lang.annotation.ElementType.PARAMETER,
                                        java.lang.annotation.ElementType.LOCAL_VARIABLE})
          public @interface Nullable 
          {
          }
          

          如果您使用正确的retention policy,则注释在运行时将不可用。从这个角度来看,这只是内部的事情。

          尽管这不是一门严格的科学,但我认为为它使用 internal 类是最有意义的。

          • 这是一个内部问题。 (无功能或技术影响)
          • 有很多很多的用法。
          • IDE 之类的 IntelliJ 支持自定义 @Nullable/@NotNull 注释。
          • 大多数框架也更喜欢使用自己的内部版本。

          其他问题(见 cmets):

          如何在 IntelliJ 中进行配置?

          点击IntelliJ状态栏右下角的“警察”。然后单击弹出窗口中的“配置检查”。下一个 ...

          【讨论】:

          • 我尝试了你的建议,但ideatest(null); 调用的void test(@NonNull String s) {} 只字不提
          • @user1244932 你是说 IntelliJ IDEA 吗?您可以配置它用于静态分析的可空性注释。我不完全知道在哪里,但定义它们的地方是“文件 > 设置 > 构建、执行、部署 > 编译器”,其中有一个“配置注释...”按钮。
          • @user1244932 如果您仍在寻找此内容,请查看屏幕截图。
          【解决方案19】:

          IntelliJ 的优点之一是您不需要使用它们的注释。您可以自己编写,也可以使用您喜欢的任何其他工具。你甚至不限于单一类型。如果您正在使用两个使用不同 @NotNull 注释的库,您可以告诉 IntelliJ 使用它们。为此,请转到“配置检查”,单击“恒定条件和例外”检查,然后单击“配置检查”按钮。我尽可能使用 Nullness Checker,因此我将 IntelliJ 设置为使用这些注释,但您可以使其与您想要的任何其他工具一起使用。 (我对其他工具没有意见,因为我多年来一直在使用 IntelliJ 的检查,我喜欢它们。)

          【讨论】:

            【解决方案20】:

            sun 现在没有自己的了吗?这是什么:
            http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

            这似乎与我在过去几年中使用的所有 Java 版本打包在一起。

            编辑:如下面的 cmets 所述,您可能不想使用这些。在这种情况下,我投票支持 IntelliJ jetbrains 注释!

            【讨论】:

            • 我不知道它是什么,但包名应该是一个重要的线索,它不适合一般用途。
            • 通常避免使用 com.sun 命名空间中的类,因为它们是内部的;不适合直接使用;并且不保证它们未来的可用性或行为。一个人必须有一个非常可靠的案例才能直接使用 com.sun 工件。
            • 加上以如此糟糕的 HTML 格式显示的内容(在 Java2s.com 上最重要的是)应该会给你一些危险信号 :)
            【解决方案21】:

            Spring 5 在包级别有@NonNullApi。对于已经具有 Spring 依赖项的项目,这似乎是一个方便的选择。所有字段、参数和返回值默认为@NonNull 和@Nullable 可以应用在少数不同的地方。

            文件包信息.java:

            @org.springframework.lang.NonNullApi
            package com.acme;
            

            https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.nullability.annotations

            【讨论】:

              【解决方案22】:

              另一个选项是 ANTLR 4 提供的注释。在Pull Request #434 之后,包含@NotNull@Nullable 注释的工件包括一个注释处理器,该处理器会在其中一个事件中产生编译时错误和/或警告属性被滥用(例如,如果两者都应用于同一个项目,或者如果@Nullable 应用于具有原始类型的项目)。注释处理器在软件开发过程中提供了额外的保证,即这些注释的应用所传达的信息是准确的,包括在方法继承的情况下。

              【讨论】:

                【解决方案23】:

                JSpecify 现在是要走的路。事实上:their presentation 主动链接到这个问题,并指定他们的目标是让它最终得到一个好的答案。

                它有 Android、Guava 和 Kotlin 等主要参与者。

                【讨论】:

                • 现在?他们的 repo 和站点并没有给人以它可用的印象。 maven 上似乎有 v0.2.0,但 repo 中没有。
                • 您可以使用org.jspecify:jspecify:0.2.0,它实际上有2个注释。见github.com/jspecify/jspecify/releases 因为它仍然是一个测试版,他们强调不要在图书馆中使用它。
                【解决方案24】:

                较新的项目可能应该使用jakarta.annotation-apijakarta.annotation 包)。
                它从现在开始以只读方式链接javax.annotation repo,并适合新的 jakarta 生态系统,旨在让社区摆脱所有与javax 相关的麻烦。

                【讨论】:

                  【解决方案25】:

                  如果您使用的是 Spring 5.x / SpringBoot 2.x,您绝对应该使用 Spring 注释 (org.springframework.lang),因为它们为您提供了默认的包范围空值检查,带有注释 @NonNullFields 和 @ 987654323@。您甚至不会与来自其他依赖项的其他 NotNull/NonNull 注释发生冲突,因为您正在使用 @NonNullFields/@NonNullApi。注释必须在一个名为 package-info.java 的文件中使用,该文件位于包的根目录中:

                  @NonNullFields
                  @NonNullApi
                  package org.company.test;
                  

                  要从 null 检查中排除某些字段、参数或返回值,只需显式使用 @Nullable 注释即可。除了使用@NonNullFields/@NonNullApi,您还可以在任何地方设置@NonNull,但默认情况下使用@NonNullFields/@NonNullApi 激活空检查可能更好,并且只使用@Nullable 执行特定异常。

                  IDE (Intellij) 将突出显示违反 null 条件的代码。如果设置正确,每个开发人员都可以假设字段、参数和返回值不能为空,除非有 @Nullable 注释。如需更多信息,请查看此article

                  【讨论】:

                    猜你喜欢
                    • 2010-09-17
                    • 2015-01-25
                    • 2013-06-30
                    • 1970-01-01
                    • 2012-02-02
                    • 2014-03-25
                    • 2014-03-23
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多