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

Which @NotNull Java annotation should I use?

提问人:jaxzin 提问时间:2/11/2011 最后编辑:Adam Burleyjaxzin 更新时间:10/12/2023 访问量:410779

问:

我希望使我的代码更具可读性,并使用 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 用于静态分析。
    文档

  • 龙目岛。NonNull
    用于控制 Project Lombok 中的代码生成。
    占位符注释,因为没有标准。
    来源文档

  • androidx.annotation.NonNull
    Android 中提供的标记注释,由注释包
    文档提供

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

java nullpointerException 注释 IDE

评论

258赞 irreputable 2/11/2011
Apache 应该发明一个“通用”注解和一个可以将其转换为任何其他注解的工具。解决标准过多问题的办法是发明一个新标准。
10赞 ymajoros 10/27/2012
@irreputable,如果Apache发明了一个新的“通用”,它将有56个版本,与其他项目重叠。而且,无论如何它都不会是标准的(标准!=广泛)。最好使用任何真正标准的东西,javax?。* .顺便说一句,这些例子中没有“太多标准”,我只看到 1 或 2 个。
8赞 Nicolas C 7/18/2013
javax.annotation.Nonnull 确实适用于 findbugs(刚刚测试过),这是我使用它的一个令人信服的理由。
26赞 Thomas Weller 3/6/2015
如果我简单地写@NotNull,它指的是.我的天啊......com.sun.istack.internal.NotNull
4赞 Dave 7/20/2016
@MozartBrocchini - 在以前可能使用过 NullObjects 的情况下,可选选项很有用。不过,它们并没有真正解决与运行时 \@NotNull 注释相同的目标,并且它们引入了繁琐的解包。

答:

63赞 Sam Barnum 2/11/2011 #1

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

评论

75赞 Daniel Alexiuc 8/26/2011
更新:IntelliJ 现在支持上述所有用于代码突出显示的注释,因此您不再局限于 IntelliJ 的注释:blogs.jetbrains.com/idea/2011/03/...
7赞 Martin 7/31/2013
javax.annotation.Nonnull被更广泛地接受,不是吗?
1赞 Hakanai 11/24/2014
@DanielAlexiuc 但不幸的是,它没有将它们用于运行时检查,因此使用 JetBrains 仍然有好处......
4赞 Karol S 3/7/2017
@Trejkaz 自 2016.3 以来,它为所有这些创建运行时检查。
1赞 Pwnstar 4/12/2022
爪哇 17...还是期待那个哈哈哈
33赞 Stephen C 2/11/2011 #2

根据 Java 7 特性列表,JSR-308 类型注解被推迟到 Java 8。甚至没有提到 JSR-305 注解。

在最新的 JSR-308 草案的附录中有一些关于 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 的幕后推手之一。

评论

4赞 Stephen C 7/19/2012
@pst - 目前 Java 8 的发布计划是 2013 年 9 月正式发布 - infoq.com/news/2012/04/jdk-8-milestone-release-dates
2赞 Stephen C 5/10/2013
现在已经下滑到2014年3月-openjdk.java.net/projects/jdk8。JSR 308 包含在 M7 构建中(参见 “104 - Java 类型的注解”)。
2赞 Nate W. 2/11/2011 #3

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

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

编辑:如下面的评论中所述,您可能不想使用这些。在这种情况下,我投票给 IntelliJ jetbrains 注解!

评论

11赞 Stephen C 2/11/2011
我不知道它是什么,但软件包名称应该是一个重要的线索,表明它不适合一般用途。
3赞 luis.espinal 2/11/2011
人们通常避免使用 com.sun 命名空间中的类,因为它们是内部的;不打算直接使用;并且不保证它们未来的可用性或行为。必须有一个非常可靠的案例才能直接使用 com.sun 工件。
0赞 luis.espinal 2/11/2011
加上以如此糟糕的 HTML 格式显示的内容(最重要的是 Java2s.com)应该会给您带来一些危险信号:)
100赞 Bert F 2/11/2011 #4

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

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

我喜欢这个系统的四点:

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

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

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

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

  3. 该框架允许您在不创建对框架的依赖的情况下使用,方法是将注释包含在注释中:例如.这很好,因为您可以注释和检查库或共享代码,但仍然能够在另一个不使用框架的项目中使用该库/共享编码。这是一个很好的功能。我已经习惯了使用它,尽管我现在倾向于在我的所有项目上启用 Checker 框架。/*@Nullable*/

  4. 该框架有一种方法可以通过使用存根文件来注释你使用的 API,这些 API 尚未注释为空。

评论

4赞 Burkhard 12/4/2012
看起来很棒,我想使用它,但不能。为什么选择GPL?难道不能是LGPL吗?
15赞 seanf 7/11/2013
根据常见问题解答:“更宽松的 MIT 许可证适用于您可能希望包含在自己的程序中的代码,例如注释。
1赞 Paul Wagland 10/21/2016
链接当前已损坏。但是 +1 关于使用 Checker Framework 的建议。
1赞 Franklin Yu 1/22/2017
遗憾的是,不可变性检查器在最新版本中被删除了。
2赞 Quazi Irfan 5/16/2018
Oracle Java 教程中也建议使用 Checker Framework。
5赞 Arto Bendiken 7/26/2011 #5

在等待上游(Java 8?)整理出来时,你也可以定义你自己的项目本地和注解。如果您使用的是 Java SE,这在默认情况下不可用时也很有用。@NotNull@Nullablejavax.validation.constraints

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

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

11赞 Ophir Radnitz 8/1/2011 #6

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

12赞 Horcrux7 9/17/2011 #7

Eclipse 也有自己的注解。

org.eclipse.jdt.annotation.NonNull

有关详细信息,请参见 http://wiki.eclipse.org/JDT_Core/Null_Analysis

评论

0赞 Motti Strom 6/15/2012
看起来这将从 Eclipse 3.8 (Juno) 集成,这将使 Eclipse 在这方面与 IntelliJ 保持一致。此外,它应该允许您配置自己的 Null 注释(例如 javax.annotation.Nonnull),并可以选择将 NotNull 设置为默认值。
19赞 Gili 11/23/2012 #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,但随附的 JSR 定义了。在撰写本文时,由于以下错误,Maven 插件无法使用:https://github.com/typetools/checker-framework/issues/183Checkers Framework

评论

2赞 Marc von Renteln 7/24/2013
修复了 maven 的 showstopper 问题。所以这应该再次成为一个选项。
0赞 Christophe Roussy 7/25/2016
我通过 Maven 使用 FindBugs,我的 IDE 什么也没做,这避免了 IDE 特定的注释,您有什么建议?
0赞 Gili 7/26/2016
@ChristopheRoussy 您的问题是特定于 IDE 的。请单独提出一个问题。
7赞 Werner Keil 4/1/2013 #9

不幸的是,不会添加比这个项目更多的值 本地 不为空 建议在这里JSR 308

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

它背后没有商业力量,因此现在推出(早期草案审查),而应该在不到 6 个月的时间内发货:-O 类似于顺便说一句。但与此不同的是,它现在已经远离了它的创始人,以尽量减少它对 Java 平台造成的伤害。JSR 308EDR 3JCPJava 8310308 Oracle

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

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

4赞 chaqke 5/1/2013 #10

如果你正在为 android 开发,那么你在某种程度上与 Eclipse 联系在一起(编辑:在撰写本文时,不再是了),它有自己的注解。它包含在 Eclipse 3.8+ (Juno) 中,但默认情况下处于禁用状态。

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

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

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

我使用:

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

评论

5赞 dcow 8/13/2013
与 Eclipse 绑定?不对。
1赞 Mārtiņš Briedis 10/14/2013
我认为,@DavidCowden支持 Android 开发的 IntelliJ IDEA 在 AndroidStudio 推出之前就已经上市了。
0赞 dcow 10/16/2013
@MārtiņšBriedis是的,这是真的。我想你的意思是.@chaqke
0赞 chaqke 10/16/2013
值得注意的是,Android 和 Intellij 有单独的注解,并且可能会保持这种状态,直到 Java 包含官方注解。这些是将 Eclipse 的注解与 Eclipse 一起使用的说明。
0赞 DennisK 1/4/2014
它从未与Eclipse联系在一起。您可以使用任何您想要的 IDE。
1赞 Sam Harwell 1/22/2014 #11

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

20赞 Bruno Eberhard 4/25/2014 #12

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

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

评论

0赞 Ali 4/1/2016
是的,这是导致 Intellij 抛出警告的原因。
0赞 BamaPookie 5/17/2017
当前版本(截至 05/2017)是 15.0
0赞 Bruno Eberhard 5/18/2017
你的权利。我已经更新了版本。即使我猜它没有太大变化。
0赞 Peter Major 11/23/2017
请记住,JetBrains 注解不会保留到运行时,因此 Guice @Nullable 支持不适用于它。
28赞 James Wald 9/5/2014 #13

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

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

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

评论

3赞 apricot 4/23/2015
为这项建议提供理由将是有益的。
2赞 James Wald 4/23/2015
tools.android.com/tech-docs/support-annotations“支持库本身也已使用这些注释进行注释,因此作为支持库的用户,Android Studio 已经会检查您的代码并根据这些注释标记潜在问题。”
3赞 CAMOBAP 8/5/2017
顺便说一句,Android Studio 还支持带有注释的 jsr305javax.annotation.*
7赞 Shubham Chaudhary 10/11/2015 #14

人造人

这个答案是特定于 Android 的。Android 有一个名为 的支持包。这提供了数十特定于 Android 的注释,也提供了常见的注释,例如 等。support-annotationsNonNullNullable

如需添加 support-annotations 软件包,请在 build.gradle 中添加以下依赖项:

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

然后使用:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}
6赞 Mozart Brocchini 5/4/2016 #15

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

  1. 通过将可为 null 的字段包装起来,使可为 null 的字段显式使用类型java.util.Optional
  2. 检查所有不可为空的字段在构造时是否为空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 时甚至需要注释吗?

编辑:我后来发现,有些人认为在参数中使用是一种不好的做法,这里有一个很好的讨论,有利弊 为什么 Java 8 的可选不应该在参数中使用Optional

推荐的选项,因为在参数中使用 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;} 
 }

评论

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

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

对于不应为 null 的内容:

  • 运行时检查:使用 “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 或 checkers用于静态检查的框架注解比 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);
    }
}

请注意,当取消引用可为 null 的方法参数时,无法使 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(编写正确的代码),同时在传递时返回有用的错误。

评论

0赞 Stephan Herrmann 10/8/2019
我现在才找到这个答案,但是@tkruse,你在哪里找到这个:“Eclipse jdt 注解不适用于静态方法返回和其他一些情况”?(第一部分不是真的,第二部分很模糊:))。
0赞 tkruse 10/8/2019
@StephanHerrmann:我不记得了。我删除了要点。
6赞 bvdb 2/7/2017 #17

如果你正在做一个大项目,你可能会更好地创建自己的和/或注释。@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 
{
}

如果使用正确的保留策略,则批注在运行时将不可用。从这个角度来看,这只是一个内部的事情。

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

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

其他问题(见评论):

如何在 IntelliJ 中配置它?

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

评论

1赞 user1244932 3/3/2017
我尝试了你的建议,但什么也没说ideavoid test(@NonNull String s) {}test(null);
3赞 Adowrath 3/24/2017
@user1244932 你是说 IntelliJ IDEA 吗?您可以配置它用于静态分析的可空性注释。我不确切知道在哪里,但定义它们的一个地方是“文件>设置>构建、执行、部署>编译器”中,那里有一个按钮“配置注释...”。
0赞 bvdb 10/27/2017
@user1244932 如果您仍在寻找这个,请参阅屏幕截图。
423赞 Ludwig Weinzierl 3/9/2017 #18

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

这给我们留下了 or . 前者带有 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.注解 ✔️ ✔️ ✔️ ✔️
龙目岛 ✔️ ✔️ ✔️ ✔️
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 Framework 支持。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

评论

13赞 robinst 3/10/2017
缺点是它 a) 基于一个死的 JSR,b) 很难找到一个只提供注释并得到维护的工件。来自 findbugs 的那个不是:search.maven.org/......javax.annotation
31赞 robinst 9/28/2017
反对的另一点是它会导致 Java 9 出现问题,因为其他模块也在该包 (jax-ws) 中提供类。javax.annotation
13赞 robinst 11/27/2017
@kevinarpe:Findbugs 项目已经死了,后续项目 Spotbugs 正在删除这些注解: github.com/spotbugs/spotbugs/pull/180
15赞 Mark Reinhold 7/26/2018
JSR 305 本来是标准化的,但从未完成,因为它的规范领先者已经 AWOL。这与甲骨文的任何决定无关。javax.annotation.NonNull
13赞 Flow 4/8/2019
不使用 jsr305.jar 的另一个原因是它显然违反了 Oracle Java 二进制许可证: github.com/google/guava/issues/2960
5赞 walkeros 5/31/2017 #19

如果您使用 Spring Framework 构建应用程序,我建议使用打包在以下依赖项中的来自 Beans 验证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. 提供用于 Bean 验证的 API JAR,以及 JSR-303/JSR-349 注解的验证器实现的 jar(随 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

还可以验证返回值。

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

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

评论

2赞 koppor 3/29/2020
请不要使用它。它用于运行时验证,而不是静态代码分析。有关详细信息,请参见 justsomejavaguy.blogspot.com/2011/08/...。资料来源:已删除 答案 219 由 @luis.espinal.
0赞 walkeros 4/6/2020
@koppor:我不同意。如果这不是为了使用,为什么 Spring 会在运行时处理它。此外,Beans 验证框架允许创建纯粹用于运行时分析的注解,因为它允许在运行时访问 Context 对象(当前已注解/验证的 instancje)。
5赞 MiguelMunoz 7/17/2018 #20

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

7赞 noamtm 5/28/2019 #21

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

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

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

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

Maven的:

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

Gradle:

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

评论

4赞 skagedal 10/13/2019
嗯,这说明不是这样:kotlinlang.org/docs/reference/......
0赞 Joe 10/4/2020
具体来说,Kotlin 还记录了对 、 和 的支持,以及包括 和 的实现。javax.annotationedu.umd.cs.findbugs.annotationsorg.eclipse.jdt.annotationlombok.NonNullorg.checkerframeworkio.reactivex.annotations
2赞 ken jarrad 5/11/2020 #22

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

文件包-info.java:

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

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

12赞 Cristan 8/25/2021 #23

JSpecify 将是要走的路(当它准备好时)。事实上:他们的演讲积极地链接到这个问题,并指出他们的目标是让它最终得到一个好的答案。

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

评论

1赞 gix 8/25/2021
现在?他们的 repo 和网站并没有给人一种它尚可用的印象。maven 上似乎有一个 v0.2.0,但 repo 中什么都没有。
1赞 Cristan 10/5/2021
您可以使用它实际上有 2 个注释。请参阅 github.com/jspecify/jspecify/releases 由于它仍然是一个测试版,他们强调不要在库中使用它。org.jspecify:jspecify:0.2.0
0赞 koppor 8/29/2023
同时,v0.3.0 发布了一个很好的用户指南:jspecify.dev/docs/user-guide。对于IntelliJ IDE的“专业”功能,支持尚未完全存在,但将得到修复:youtrack.jetbrains.com/issue/IDEA-323691/... (请投票)@NullMarked
6赞 morgwai 10/15/2021 #24

较新的项目可能应该使用 jakarta.annotation-api(包)。
它与现在只读的javax.annotation存储库链接,并适合新的jakarta生态系统,该生态系统旨在将社区从所有相关的头痛中解放出来。
jakarta.annotationjavax

评论

3赞 gavenkoa 4/26/2023
Jakarta 注解 API 是未来的发展方向:& .它们是标准的,并且具有通用语义。再见 Javax 验证 API 注解!jakarta.annotation.Nonnulljakarta.annotation.Nullable
3赞 maxeh 11/2/2021 #25

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

@NonNullFields
@NonNullApi
package org.company.test;

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

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

评论

0赞 Vic Stalkeur 10/15/2023
谢谢你的信息。我已将这些添加到多个包中,但是当我传递空值时,我没有看到任何警告。