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

我正在寻找使我的代码更具可读性,并使用工具如IDE代码检查和/或静态代码分析(FindBugs和声纳),以避免NullPointerExceptions。 许多工具看起来与@NotNull / @NonNull / @Nonnull注解不兼容,并且在我的代码中列出所有这些工具将会很糟糕。 任何建议哪一个是最好的? 以下是我找到的等效注释列表:

  • javax.validation.constraints.NotNull
    创建运行时验证,而不是静态分析。
    文件

  • edu.umd.cs.findbugs.annotations.NonNull
    Findbugs使用静态分析,因此Sonar(现在是Sonarqube )
    文件

  • javax.annotation.Nonnull
    这可能也适用于Findbugs,但JSR-305是不活动的。 (另见: JSR 305的状态是什么? ) 来源

  • org.jetbrains.annotations.NotNull
    IntelliJ IDEA IDE用于静态分析。
    文件

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

  • android.support.annotation.NonNull
    Android中提供的标记注释,由支持注释包提供
    文件

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

我非常喜欢Checker框架 ,它是一个类型注释( JSR-308 )的实现,它被用来实现缺陷检查器,比如一个无效检查器。 我还没有真正尝试过其他人提供任何比较,但我很高兴与此实施。

我不提供该软件的团体,但我是一个粉丝。

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

  1. 它有一个缺陷检查器(null)(@Nullable),但也有一些不变性和实习 (和其他)。 我使用第一个(零度),我试图进入使用第二个(不变性/ IGJ)。 我正在尝试第三个,但我不确定长期使用它。 我并不确信其他棋子的普遍用处,但是我们很高兴知道框架本身就是一个实现各种附加注释和检查器的系统。

  2. 空值检查的默认设置工作正常:非本地除外(NNEL)。 基本上,这意味着默认情况下,检查器会将除局部变量以外的所有元素(实例变量,方法参数,泛型类型等)视为缺省为@NonNull类型。 根据文档:

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

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

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

  4. 该框架有一种方法可以使用存根文件注释您使用的API尚未注释为空。

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

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

由于Oracle决定暂时不对@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.constraints或javax.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))。

正如本文中所述,运行时注释的影响比人们想象的要小,但是除了编译时间之外,它们还具有启用运行时检查工具的好处。

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


                                字段方法参数LOCAL_VARIABLE 
 android.support.annotation XXX
 edu.umd.cs.findbugs.annotations XXXX
 org.jetbrains.annotation XXXX
龙目岛XXXX
 javax.validation.constraints XXX

org.eclipse.jdt.annotation,javax.annotation和org.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,所以我将它们分开列出lombok有一个很无聊的@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框架实现的java.annotation语法。

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

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


来源

  • 来自android-5.1.1_r1.jar的android.support.annotation
  • findbugs-annotations-1.0.0.jar中的edu.umd.cs.findbugs.annotations
  • 来自org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar的org.eclipse.jdt.annotation
  • 来自jetbrains-annotations-13.0.jar的org.jetbrains.annotations
  • 来自gwt-dev-2.5.1-sources.jar的javax.annotation
  • org.checkerframework.checker.nullness.qual from checker-framework-2.1.9.zip
  • 来自龙目岛的龙目岛承诺f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • 来自validation-api-1.0.0.GA-sources.jar的javax.validation.constraints

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

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

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

JSR305和FindBugs是由同一个人创作的。 两者都维护得不好,但都是标准的,并得到所有主要IDE的支持。 好消息是,他们现在工作的很好。

下面是如何将@Nonnull默认应用于所有的类,方法和字段。 请参阅https://stackoverflow.com/a/13319541/14731和https://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插件由于这个bug而无法使用: https : //github.com/typetools/checker-framework/issues/183

如果有人只是在寻找IntelliJ类:你可以从Maven仓库获取它们

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

只需指出Java验证API( javax.validation.constraints.* )不带有@Nullable注释,这在静态分析上下文中非常有用。 这对运行时bean验证是有意义的,因为这是Java中任何非原始字段的默认值(即没有任何验证/强制)。 为了说明的目的应该权衡替代品。

Eclipse也有自己的注释。

 org.eclipse.jdt.annotation.NonNull 

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

不幸的是, JSR 308不会在这里添加更多的值,比这个项目的Local Null建议更多

Java 8不会带有单个默认注释或自己的Checker框架。 类似于Find-bugs或JSR 305 ,这个JSR很少被大部分学术团队维护。

没有商业力量,因此JSR 308推出EDR 3 (早期草案审查),而Java 8应该在不到6个月的时间内发布:-O类似于310 btw。 但与308 Oracle不同的是, 308 Oracle公司现在已经从其创始人那里负责,以尽量减少对Java平台的损害。

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

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

在等待上游(Java 8?)进行排序的同时,您还可以定义自己的项目本地@NotNull@Nullable注释。 如果您使用的是Java SE,其中缺省情况下javax.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 {} 

无可否认,这主要是出于装饰性或面向未来的目的,因为上述明显不会增加对这些注释的静态分析的支持。

如果你正在为android开发,那么你会绑定到Eclipse上(编辑:在写作的时候,不再是了),它有自己的注释。 它包含在Eclipse 3.8+(Juno)中,但默认情况下是禁用的。

您可以在首选项> Java>编译器>错误/警告>空分析(底部的可折叠部分)中启用它。

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

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage有关于设置的建议&#x3002; 但是,如果您的工作空间中有外部项目(如Facebook SDK),则可能无法满足这些建议,并且您可能不希望每个SDK更新都解决这些问题;-)

我用:

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

Android的

这个答案是Android特定的。 Android有一个名为support-annotations支持包。 这提供了许多 Android特定的注释,并且还提供了常见的 NonNullNullable等。

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

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

然后使用:

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

在Java 8中还有另外一种方法。我正在做两件事来完成我所需要的:

  1. 通过使用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注释?

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

如果你正在做一个大项目,你可能会更好地创建自己的 @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状态栏右下角的“警察”。 然后点击弹出窗口中的“配置检查”。 下一个 … 配置注释

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

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

编辑:正如在下面的评论中提到的,你可能不希望使用这些。 在这种情况下,我的投票是针对IntelliJ jetbrains注释!

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

对于不应该为null的东西:

  • 静态检查:使用@ javax.annotations.Nonnull
  • 运行时检查:使用“if(x == null)…”(零依赖关系)或@ javax.validation.NotNull(使用bean验证)或@ lombok.NonNull(普通和简单)或者guavas Preconditions.checkNotNull 。)
  • 在适合的地方,在类或包级别上使用@ … NonnullByDefault注释。 自己创建这些注释(例子很容易找到)。

对于可能为空的内容(不需要运行时检查):

  • 使用可选的方法返回类型(仅)。 Java8或番石榴。
  • 否则,使用@ javax.annotation.CheckForNull来避免NPE

这应该给出最好的结果:IDE中的警告,FindBugs和checkerframework的错误,有意义的运行时异常。

一些解释:

  • jetbrains, eclipse or checkersframework annotations for static checking have no advantage over javax.annotations. jetbrains @NotNull name conflicts with @javax.validation.NotNull.
  • @javax.annotations.Nullable does not mean what you (or your IDE) think it means. Findbugs will ignore it (on members). Sad, but true.
  • For @javax.annotations.CheckForNull, you may have to configure your IDE, or else use it in combination with @javax.annotations.Nullable
  • For static checking, 2 free tools exist: Findbugs and checkersframework.
  • The Eclipse library has @NonNullByDefault, jsr305 only has @ParametersAreNonnullByDefault. Those are mere convenience wrappers applying base annotations to everythin in a package (or class), you can easily create your own. This can be used on package. This may conflict with generated code (eg lombok).
  • Using lombok as an exported dependency should be avoided for libraries that you share with other people, the less transitive dependencies, the better
  • Using Bean validation framework is powerful, but requires high overhead, so that's overkill just to avoid manual null checking.
  • Using Optional for fields and method parameters is controversial (you can find articles about it easily)
  • I don't know about Android, check other answers

If you are building your application using Spring Framework I would suggest using javax.validation.constraints.NotNull comming from Beans Validation packaged in following dependency:

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

The main advantage of this annotation is that Spring provides support for both method parameters and class fields annotated with javax.validation.constraints.NotNull . All you need to do to enable support is:

  1. supply the api jar for beans validation and jar with implementation of validator of jsr-303/jsr-349 annotations (which comes with Hibernate Validator 5.x dependency):

     <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. provide MethodValidationPostProcessor to spring's context

      @Configuration @ValidationConfig public class ValidationConfig implements MyService { @Bean public MethodValidationPostProcessor providePostProcessor() { return new MethodValidationPostProcessor() } } 
  3. finally you annotate your classes with Spring's org.springframework.validation.annotation.Validated and validation will be automatically handled by Spring.

例:

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

When you try calling method doSomething and pass null as the parameter value, spring (by means of HibernateValidator) will throw ConstraintViolationException . No need for manuall work here.

You can also validate return values.

Another important benefit of javax.validation.constraints.NotNull comming for Beans Validation Framework is that at the moment it is still developed and new features are planned for new version 2.0.

What about @Nullable ? There is nothing like that in Beans Validation 1.1. Well, I could arguee that if you decide to use @NotNull than everything which is NOT annotated with @NonNull is effectively "nullable", so the @Nullable annotation is useless.

Another option is the annotations provided with ANTLR 4. Following Pull Request #434 , the artifact containing the @NotNull and @Nullable annotations includes an annotation processor that produces compile-time errors and/or warnings in the event one of these attributes is misused (for example, if both are applied to the same item, or if @Nullable is applied to item with a primitive type). The annotation processor provides additional assurance during the software development process that the information conveyed by the application of these annotations is accurate, including in cases of method inheritance.