我希望使我的代码更具可读性,以及使用工具,如IDE代码检查和/或静态代码分析(FindBugs和Sonar),以避免nullpointerexception。许多工具似乎与彼此的@NotNull/@NonNull/ @NonNull注释不兼容,在我的代码中列出所有这些工具会让人难以阅读。有没有什么建议,哪一个是“最好的”?下面是我找到的等价注释列表:

javax.validation.constraints.NotNull Created for runtime validation, not static analysis. documentation edu.umd.cs.findbugs.annotations.NonNull Used by FindBugs (dead project) and its successor SpotBugs static analysis and therefore Sonar (now Sonarqube) FindBugs documentation, SpotBugs documentation javax.annotation.Nonnull This might work with FindBugs too, but JSR-305 is inactive. (See also: What is the status of JSR 305?) source org.jetbrains.annotations.NotNull Used by IntelliJ IDEA IDE for static analysis. documentation lombok.NonNull Used to control code generation in Project Lombok. Placeholder annotation since there is no standard. source, documentation androidx.annotation.NonNull Marker annotation available in Android, provided by annotation package documentation org.eclipse.jdt.annotation.NonNull Used by Eclipse for static code analysis documentation


当前回答

Eclipse也有自己的注释。

org.eclipse.jdt.annotation.NonNull

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

其他回答

JSR305和FindBugs是由同一个人编写的。两者的维护都很差,但都是标准的,并且得到所有主要ide的支持。好消息是,他们工作得很好。

下面是如何在默认情况下将@Nonnull应用到所有类、方法和字段。 参见https://stackoverflow.com/a/13319541/14731和https://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框架定义了。在撰写本文时,由于这个错误,Maven插件无法使用:https://github.com/typetools/checker-framework/issues/183

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

对于不应该为null的东西:

Runtime check: Use "if (x == null) ..." (zero dependency) or @javax.validation.NotNull (with bean validation) or @lombok.NonNull (plain and simple) or guavas Preconditions.checkNotNull(...) Use Optional for method return types (only). Either Java8 or Guava. Static check: Use an @NonNull annotation Where it fits, use @...NonnullByDefault annotations on class or package level. Create these annotations yourself (examples are easy to find). Else, use @...CheckForNull on method returns to avoid NPEs

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

不要期望静态检查是成熟的,它们的命名不是标准化的,不同的库和ide对待它们是不同的,忽略它们。JSR305 java .annotations。*类看起来像标准的,但它们不是,它们在Java9+中会导致拆分包。

一些注释解释:

Findbugs/spotbugs/jsr305 annotations with package javax.validation.* clash with other modules in Java9+, also possibly violate Oracle license Spotbugs annotations still depends on jsr305/findbugs annotations at compiletime (at the time of writing https://github.com/spotbugs/spotbugs/issues/421) jetbrains @NotNull name conflicts with @javax.validation.NotNull. jetbrains, eclipse or checkersframework annotations for static checking have the advantage over javax.annotations that they do not clash with other modules in Java9 and higher @javax.annotations.Nullable does not mean to Findbugs/Spotbugs what you (or your IDE) think it means. Findbugs will ignore it (on members). Sad, but true (https://sourceforge.net/p/findbugs/bugs/1181) For static checking outside an IDE, 2 free tools exist: Spotbugs(formerly Findbugs) and checkersframework. The Eclipse library has @NonNullByDefault, jsr305 only has @ParametersAreNonnullByDefault. Those are mere convenience wrappers applying base annotations to everything in a package (or class), you can easily create your own. This can be used on package. This may conflict with generated code (e.g. 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) Android null annotations are part of the Android support library, they come with a whole lot of other classes, and don't play nicely with other annotations/tools

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

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


// file: PublicApi
package example;

public interface PublicApi {

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

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

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

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

注意,当可以为空的方法参数被解引用时,没有办法让Spotbugs引发警告(在撰写本文时,Spotbugs是3.1版)。也许checkerframework可以做到。

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

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

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

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

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

文件package-info.java:

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

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

如果有人只是在寻找IntelliJ类:您可以从maven存储库中使用

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

安卓

这个答案是Android特有的。Android有一个支持包叫做support-annotations。它提供了几十个Android特定的注释,也提供了常见的注释,如NonNull, Nullable等。

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

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

然后使用:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}