ChatGPT解决这个技术问题 Extra ChatGPT

我应该使用哪个 @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 Marker 注解在Android中可用,由注解包文档提供

org.eclipse.jdt.annotation.NonNull 被 Eclipse 用于静态代码分析文档

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

I
Ian Boyd

由于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))。

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

另一个重要的区别是可以在代码中的何处使用注释。有两种不同的方法。一些包使用 JLS 9.6.4.1 风格的上下文。下表提供了一个概述:

Package FIELD METHOD PARAMETER LOCAL_VARIABLE android.support.annotation ✔️ ✔️ ✔️ edu.umd.cs.findbugs.annotations ✔️ ✔️ ✔️ ✔️ org.jetbrains.annotation ✔️ ✔️ ✔️ ✔️ lombok ✔️ ✔️ ✔️ ✔️ javax.validation.constraints ✔️ ✔️ ✔️

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

这给我们留下了

javax.annotation

org.checkerframework.checker.nullness.qual

在这一轮中。

代码

为了帮助您自己比较更多细节,我在下面列出了每个注释的代码。为了便于比较,我删除了评论、导入和 @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();
    }
}

支持

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

概括

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

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

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

来源

来自 android-5.1.1_r1.jar 的 android.support.annotation

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

来自 gwt-dev-2.5.1-sources.jar 的 javax.annotation

org.checkerframework.checker.nullness.qual 来自 checker-framework-2.1.9.zip

来自 lombok 的 lombok 提交 f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4

来自 validation-api-1.0.0.GA-sources.jar 的 javax.validation.constraints


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,但由于其规范负责人擅离职守,因此从未完成。这与甲骨文的任何决定无关。
不使用 jsr305.jar 的另一个原因是它显然违反了 Oracle Java 二进制许可证:github.com/google/guava/issues/2960
m
mernst

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

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

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

它有一个空值缺陷检查器(@Nullable),但也有一个用于不变性和实习(以及其他)的缺陷检查器。我使用第一个(nullness),我正在尝试使用第二个(不变性/IGJ)。我正在尝试第三个,但我不确定是否要长期使用它。我还不相信其他检查器的一般用途,但很高兴知道框架本身是一个用于实现各种附加注释和检查器的系统。空值检查的默认设置效果很好:非空值,除了局部变量 (NNEL)。基本上这意味着默认情况下,检查器将除局部变量之外的所有内容(实例变量、方法参数、泛型类型等)都视为默认情况下具有 @NonNull 类型。根据文档:NNEL 默认会导致代码中显式注释的数量最少。如果 NNEL 不适合您,您可以为类或方法设置不同的默认值。该框架允许您使用 with 而无需创建对框架的依赖,方法是将您的注释包含在注释中:例如 /*@Nullable*/。这很好,因为您可以注释和检查库或共享代码,但仍然可以在另一个不使用框架的项目中使用该库/共享编码。这是一个不错的功能。我已经习惯了使用它,尽管我现在倾向于在我的所有项目中启用 Checker 框架。该框架有一种方法可以通过使用存根文件来注释您使用的尚未注释为空的 API。


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

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


更新:IntelliJ 现在支持所有上述代码高亮注释,因此您不再局限于 IntelliJ 的注释:blogs.jetbrains.com/idea/2011/03/…
javax.annotation.Nonnull 被更广泛地接受,不是吗?
@DanielAlexiuc 但不幸的是,它不使用它们进行运行时检查,所以使用 JetBrains 仍然有好处......
@Trejkaz 自 2016.3 以来,它为所有这些创建运行时检查。
Java 17... 还在期待哈哈哈
m
mernst

根据 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 背后的人之一。


@pst - 目前的时间表是 Java 8 将于 2013 年 9 月正式发布 - infoq.com/news/2012/04/jdk-8-milestone-release-dates
现在已经滑到 2014 年 3 月 - openjdk.java.net/projects/jdk8。 JSR 308 包含在构建 M7 中(请参阅“104 - Java 类型的注释”)。
J
James Wald

对于 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 会根据这些注释检查您的代码并标记潜在问题。”
顺便说一句,Android Studio 也支持带有 javax.annotation.* 注释的 jsr305
t
tkruse

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

对于不应该为空的东西:

运行时检查:使用“if (x == null) ...”(零依赖)或@javax.validation.NotNull(带有bean验证)或@lombok.NonNull(简单明了)或guava Preconditions.checkNotNull(.. .) 将 Optional 用于方法返回类型(仅)。 Java8 或 Guava。

将 Optional 用于方法返回类型(仅)。 Java8 或 Guava。

静态检查:使用 @NonNull 注释

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

否则,在方法返回时使用 @...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 null 注释是 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 的 3.1 版),没有办法让 Spotbugs 发出警告。也许 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:我不记得了。我删除了要点。
B
Bruno Eberhard

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

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

这是导致 Intellij 发出警告的原因,是的。
当前版本(截至 05/2017)是 15.0
你的权利。我已经更新了版本。即使我猜它并没有太大变化。
请记住,JetBrains 注释不会在运行时保留,因此 Guice @Nullable 支持例如不适用于它。
C
Community

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

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

定义@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的。请打开一个单独的问题。
M
Manos Nikolaidis

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 设为默认值。
O
Ophir Radnitz

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


B
Baby Groot

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

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

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

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

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


S
Shubham Chaudhary

安卓

这个答案是特定于 Android 的。 Android 具有名为 support-annotations 的支持包。这提供了 dozens of Android 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) {}

C
Community

在等待上游整理(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 {}

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


n
noamtm

这里已经有太多答案了,但是 (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'

嗯,这另有说明:kotlinlang.org/docs/reference/…
具体来说,Kotlin also documents support for javax.annotationedu.umd.cs.findbugs.annotationsorg.eclipse.jdt.annotationlombok.NonNull,以及 the implentation also including org.checkerframeworkio.reactivex.annotations
M
Mozart Brocchini

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

通过使用 java.util.Optional 包装可空字段使可空字段显式使用类型使用 java.util.Objects.requireNonNull 检查所有非可空字段在构造时不为空

例子:

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

    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 集成到构建过程中,以鼓励和突出,甚至控制合并前的代码质量问题,例如在拉取请求上。
Optional 并不意味着用作方法参数或私有字段。参见例如:stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
@assylias 是的,后来我发现了,他们说不推荐,因为它不会给我们买任何东西,我绝对能理解他们的理性。在我放在这里的这种情况下,可以使参数 description 不为空,并且客户端代码可以传递一个空字符串,但在许多情况下,区分空字符串和没有值可能很方便。谢谢你的评论。我会更新答案。
c
chaqke

如果您正在为 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 点) 潜在的空指针访问:警告(否则 facebook SDK 会有警告)空注释和空推断之间的冲突:警告(链接到第 3 点)


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

如果您使用 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 注解的方法参数和类字段的支持。启用支持所需要做的就是:

提供 api jar 用于 beans 验证和 jar 实现 jsr-303/jsr-349 注释的验证器(Hibernate Validator 5.x 依赖项附带): javax.validation validation-api 1.1.0.Final org.hibernate hibernate-validator 5.4 .1.Final 将 MethodValidationPostProcessor 提供给 spring 的上下文 @Configuration @ValidationConfig public class ValidationConfig implements MyService { @Bean public MethodValidationPostProcessor providePostProcessor() { return new MethodValidationPostProcessor() } } 最后你用 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。这里不需要手动工作。

您还可以验证返回值。

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

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


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

如果您正在处理一个大型项目,最好创建您自己的 @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,则注释在运行时将不可用。从这个角度来看,它只是一个内部的东西。

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

这是一个内部的东西。 (无功能或技术影响)

有很多很多的用法。

IDE 像 IntelliJ 支持自定义 @Nullable/@NotNull 注释。

大多数框架也更喜欢使用自己的内部版本。

其他问题(见评论):

如何在 IntelliJ 中配置它?

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


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

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


N
Nate W.

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

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

编辑:正如下面评论中提到的,您可能不想使用这些。在这种情况下,我投票支持 IntelliJ jetbrains 注释!


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

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


m
maxeh

如果您使用的是 Spring 5.x / SpringBoot 2.x,则绝对应该使用 Spring 注释 (org.springframework.lang),因为它们为您提供了带有注释 @NonNullFields@NonNullApi 的默认包范围的 null 检查。您甚至不会与来自其他依赖项的其他 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


S
Sam Harwell

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


C
Cristan

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

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


现在?他们的 repo 和站点并没有给人以它可用的印象。 maven 上似乎有一个 v0.2.0,但 repo 中没有。
您可以使用 org.jspecify:jspecify:0.2.0,它实际上有 2 个注释。请参阅github.com/jspecify/jspecify/releases,因为它仍然是测试版,所以他们强调不要在库中使用它。
m
morgwai

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