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


997

我希望使我的代码更具可读性,并使用诸如IDE代码检查和/或静态代码分析(FindBugs和Sonar)之类的工具来避免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
    用于控制Lombok项目中的代码生成。
    占位符注释,因为没有标准。
    文档

  • android.support.annotation.NonNull
    支持注释包文档提供了Android中可用的标记注释

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


203
apache应该发明一个“通用”注释和可以将其转换为任何其他注释的工具。解决太多标准问题的方法是发明新标准。
无可争议的2011年

6
@apreputable如果apache发明了一个新的“ common”,它将有56个版本,与其他项目重叠。而且,无论如何这都不是标准的(标准!=广泛使用)。最好使用真正标准的东西,javax?。*。顺便说一句,有没有“太多的标准”在这些例子中,我只看到1个或2
ymajoros

6
javax.annotation.Nonnull确实可以使用findbugs(刚刚对其进行了测试),这是我使用它的一个令人信服的理由。
Nicolas C

20
如果我只写@NotNull,它指的是com.sun.istack.internal.NotNull。OMG ...
Thomas Weller

3
@MozartBrocchini-在以前使用过NullObjects的情况下,可选选项很有用。但是,它们并没有真正实现与运行时\ @NotNull注释相同的目标,并且引入了繁琐的解包。
戴夫

Answers:


205

自从JSR 305(其目标是标准化@NonNull@Nullable)已经休眠了好几年以来,恐怕没有好的答案。我们所能做的就是找到一个务实的解决方案,我的方法如下:

句法

从纯粹的风格角度来看,除了Java本身,我想避免引用任何IDE,框架或任何工具包。

这排除了:

  • 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.annotationJSE 更好,那么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并有一个实现

@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 XXX   
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
龙目岛XXXX
javax.validation.constraints XXX   

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

这给我们留下了

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

在这一轮。

为了帮助您自己比较更多详细信息,我在下面列出了每个注释的代码。为了使比较容易,我删除了注释,导入和@Documented注释。(@Documented除了Android包中的类以外,其他所有类都具有)。我重新排列了行和@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 {}

以下两个软件包没有no @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.annotationEclipse和Checker Framework至少是开箱即用的。

摘要

我理想的注释是java.annotationChecker Framework实现的语法。

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

如果您愿意购买Checker Framework,请使用它们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
  • lomboklombok提交f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraintsvalidation-api-1.0.0.GA-sources.jar

7
缺点javax.annotation是a)基于失效的JSR,b)难以找到仅提供注释并得以维护的工件。来自findbugs的不是:search.maven.org/…–
robinst

18
反对的另一点javax.annotation是,这会导致Java 9出现问题,因为其他模块也在该软件包(jax-ws)中提供了类。
robinst

10
@kevinarpe:Findbugs项目已死,后续项目Spotbugs删除了这些注释:github.com/spotbugs/spotbugs/pull/180
robinst

4
已经标准化的JSR 305javax.annotation.NonNull尚未完成,因为它的规格领先者成为AWOL。它与Oracle的任何决定无关。
Mark Reinhold '18

5
不使用jsr305.jar的另一个原因是它显然违反了Oracle Java二进制许可证:github.com/google/guava/issues/2960
Flow

91

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

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

我喜欢此系统的四件事:

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

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

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

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

  3. 通过使用注释将注释括在注释中,该框架使您可以在使用框架的情况下使用with /*@Nullable*/。很好,因为您可以注释和检查库或共享代码,但是仍然可以在不使用框架的另一个项目中使用该库/共享代码。这是一个不错的功能。我已经习惯了使用它,即使我现在倾向于在所有项目上启用Checker框架。

  4. 该框架提供了一种方法,可以使用存根文件来注释您使用的尚未为空而注释的API


3
看起来不错,我想使用它,但是不能。为什么选择GPL?难道不是LGPL吗?
Burkhard

13
根据FAQ的说法:“更宽松的MIT许可证适用于您可能希望包含在自己的程序中的代码,例如注释。”
seanf

1
链接当前已断开。但是+1为使用Checker Framework的建议。
Paul Wagland

1
遗憾的是,最新版本中删除了不变性检查器。
富兰克林·于

1
Oracle Java教程中还建议使用Checker Framework 。
Quazi Irfan

55

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


68
更新:现在的IntelliJ支持所有的代码高亮上述注释,这样你就不会仅限于的IntelliJ的注解更多:blogs.jetbrains.com/idea/2011/03/...
丹尼尔Alexiuc

31
Eclipse Juno也是如此!
2012年

5
javax.annotation.Nonnull被更广泛接受,不是吗?
马丁

1
@DanielAlexiuc但不幸的是,它没有将它们用于运行时检查,因此使用JetBrains仍然有好处...
Trejkaz 2014年

4
@Trejkaz自2016.3起,它将为所有这些创建运行时检查。
卡罗尔S

32

根据Java 7功能列表,将 JSR-308类型的注释推迟到Java8。甚至没有提到JSR-305注释。

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

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


实际上,JSR-308没有定义任何注释类型/类,并且看起来他们认为它不在范围内。(鉴于JSR-305的存在,它们是正确的)。

但是,如果JSR-308真正看起来像是要成为Java 8,那么如果对JSR-305的兴趣重新兴起,也不会感到惊讶。AFAIK,JSR-305团队尚未正式放弃他们的工作。他们已经安静了2年以上。

有趣的是,比尔·普格(JSR-305的技术负责人)是FindBugs背后的人之一。


4
@pst-当前的时间表是Java 8将于2013年9月正式发布-infoq.com/news/2012/04/jdk-8-milestone-release-dates
Stephen C

2
现在已经滑到2014年3月-openjdk.java.net/projects/jdk8。JSR 308包含在内部版本M7中(请参见“ 104-Java类型的注释”)。
斯蒂芬·C

28

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

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

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


3
为该建议提供理由是有用的。
2015年

2
tools.android.com/tech-docs/support-annotations “支持库本身也已使用这些注释进行了注释,因此作为支持库的用户,Android Studio将已经检查您的代码并根据这些注释来标记潜在的问题。”
James Wald 2015年

3
BTW Android Studio还支持带javax.annotation.*注释的
jsr305

19

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

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

是的,这是导致Intellij发出警告的原因。
点击Upvote '16

当前版本(截至05/2017)是15.0
BamaPookie

你的权利。我已经更新了版本。即使我猜它并没有太大变化。
布鲁诺·埃伯哈德

请记住,JetBrains注释不会在运行时保留,因此Guice @Nullable支持无法使用它。
彼得·梅杰

18

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。

看起来JSR 308 正在将其纳入JDK 8,尽管JSR没有定义@NotNull,但随附的文件Checkers Framework却定义了。在撰写本文时,由于以下错误,Maven插件无法使用:https : //github.com/typetools/checker-framework/issues/183


2
Maven的showtopper问题已修复。因此,这应该再次成为一种选择。
Marc von Renteln

我通过Maven使用FindBugs,我的IDE并没有做任何事情,这避免了IDE特定的注释,您会推荐什么?
Christophe Roussy

@ChristopheRoussy您的问题特定于IDE。请打开一个单独的问题。
吉利

15

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

对于不应该为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或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验证框架功能强大,但是需要高昂的开销,因此,为了避免进行手动空值检查,这是过大的杀伤力。
  • 对字段和方法参数使用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的版本为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);
        }

这有助于客户端在传递有用的错误时不传递空值(编写正确的代码)。


我现在才找到答案,但是@tkruse在哪里找到了这个答案:“ Eclipse jdt批注不适用于静态方法返回和某些其他情况”?(第一部分是不正确的,第二部分相当模糊:))。
斯蒂芬·赫尔曼

@StephanHerrmann:我不记得了。我删除了要点。
tkruse

12

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设置为默认值。
Motti Strom

11

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


7

不幸的是,JSR 308此处添加的值不会超过此项目的本地“非空”建议

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

因此,它背后没有任何商业力量,因此应在不到6个月的时间内JSR 308发布EDR 3(立即草案发布JCP),- Java 8O与310btw 类似。但是308 Oracle现在与它的创建者不同的是,它不承担起最大程度地减少对Java平台造成的损害。

每个项目,供应商和学术班级都喜欢背后的项目Checker FrameworkJSR 308并将创建自己的专有检查器注释。

在以后的几年中使源代码不兼容,直到找到一些流行的折衷办法,然后可能将其添加到Java 9或中10,或通过诸如Apache CommonsGoogle Guava;-)之类的框架进行添加


7

安卓系统

这个答案是特定于Android的。Android有名为的支持包support-annotations。这提供了几十种Android的具体注释,还提供了常见的一样NonNullNullable等等。

要添加支持注释包,请在build.gradle中添加以下依赖项:

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

然后使用:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}

5

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

诚然,这很大程度上是出于装饰性或面向未来的目的,因为以上内容显然并没有为这些注释的静态分析添加任何支持。


4

如果您是为Android开发的,则您会与Eclipse(在撰写本文时不再编辑)有所联系,后者具有自己的注释。它包含在Eclipse 3.8+(Juno)中,但默认情况下处于禁用状态。

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

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

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

我用:

  1. 空指针访问:错误
  2. 违反空规范:错误(链接到第1点)
  3. 可能的空指针访问:警告(否则,Facebook SDK会发出警告)
  4. 空注释和空推断之间的冲突:警告(链接到第3点)

4
绑定到Eclipse? 不对。
dcow

1
我认为@DavidCowden IntelliJ IDEA支持Android开发,在推出AndroidStudio之前已有一段时间。
马丁斯Briedis

@MārtiņšBriedis是的,是的。我想你的意思是@chaqke
dcow

值得注意的是,android和intellij具有单独的注释,并且可能会一直保持这种状态,直到java包含官方注释。这些是在eclipse中使用eclipse注释的说明。
chaqke

它从未与Eclipse捆绑在一起。您可以使用任何想要的IDE。
DennisK

4

如果您正在从事大型项目,则最好创建自己的 @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状态栏右下角的“警察”。然后在弹出窗口中单击“配置检查”。下一个 ... 配置注释


1
我想你的意见,但idea告诉任何关于void test(@NonNull String s) {}所谓的test(null);
user1244932

3
@ user1244932您的意思是IntelliJ IDEA吗?您可以配置用于静态分析的可空性注释。我不知道确切的位置,但是在其中定义它们的一个地方是“文件>设置>构建,执行,部署>编译器”,其中有一个按钮“配置注释...”。
Adowrath

@ user1244932查看截图,如果您仍在寻找它。
bvdb

3

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

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

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

因此,如果您开发的Java软件包也可以在Android和Kotlin中使用(并且受Android Studio和IntelliJ支持),那么最好的选择可能是JetBrains软件包。

Maven:

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

摇篮:

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

2
嗯,这就是另外一个意思:kotlinlang.org/docs/reference/…–
skagedal

3

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

  1. 通过使用以下命令包装可空字段,使可空字段具有明确的类型 java.util.Optional
  2. 在构造时检查所有不可为空的字段是否不为null 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的Optional不应该在参数中使用

鉴于不建议在参数中使用Optional,因此需要使用备用选项,我们需要2个构造函数:

  //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();
      }

我想说您仍然需要所有4个形式参数的@NotNull批注,以便静态分析检查器知道您的意图是任何一个都不为null。Java语言中尚没有任何东西可以实现这一点。如果您进行防御性编程,还应该检查说明是否不为null。
杰克辛

2
我仍然可以编写以下代码:new Role(null,null,null,null);。使用这些注释,我的IDE和静态分析将警告不能将null传递给这些参数。没有它,直到运行代码我才发现。这就是注释的价值。
杰克辛2016年

2
我也在开发人员可以使用他们喜欢的任何IDE或文本编辑器的环境中,这不是相互排斥的。然后,我们还将maven-pmd-plugin和/或SonarQube集成到构建过程中,以鼓励和突出显示甚至合并预先存在的代码质量问题,例如在请求请求时。
jaxzin

2
可选不打算用作方法参数或私有字段。参见例如:stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
assylias 16/09/29

1
@assylias是的,我后来发现,他们说不建议这样做,因为它不会买我们任何东西,我绝对可以理解他们的合理性。在这种情况下,我可以在这里放置一个参数,使其description 不为空,并且客户端代码可以传递空字符串,但是在许多情况下,区分空字符串和没有值可能很方便。谢谢你的评论。我将更新答案。
莫扎特·布罗基尼

2

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

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

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


10
我不知道它是什么,但是包名应该是一个大提示,它不适合一般使用。
斯蒂芬·C

3
通常,它们不使用com.sun命名空间中的类,因为它们是内部的。不能直接使用;并且不保证它们的未来可用性或行为。必须有一个非常可靠的案例才能直接使用com.sun工件。
luis.espinal,2011年

加上以如此差的HTML格式显示的内容(在Java2s.com上居首)应该会给您带来一些
危险

2

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


1

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


1

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

    <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和为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注释类,并且Spring org.springframework.validation.annotation.Validated将自动处理验证。

例:

@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.NotNullBeans Validation Framework的是,目前仍在开发中,并且计划为新版本2.0提供新功能。

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


1
请不要使用它。它用于运行时验证,而不是静态代码分析。有关详细信息,请参见justsomejavaguy.blogspot.com/2011/08/…。资料来源:@ luis.espinal的219票已删除的答案。
koppor

@koppor:我不同意。如果不打算使用它,那么Spring将在运行时处理它。Beans验证框架还允许纯粹为运行时分析创建注释,因为它允许在运行时访问Context对象(当前已注释/验证的实例)。
walkeros

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.