我希望使我的代码更具可读性,以及使用工具,如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


当前回答

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

对于不应该为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(编写正确的代码),同时返回有用的错误。

其他回答

只是指出Java验证API (javax.validation.constraints.*)没有提供@Nullable注释,这在静态分析上下文中非常有价值。这对于运行时bean验证是有意义的,因为这是Java中任何非基本字段的默认值(即没有需要验证/强制的内容)。为达到上述目的,应权衡备选方案。

较新的项目可能应该使用jakarta。annotation-api(雅加达。注释包)。 它从现在的只读javax链接。注释回购和适应新的Jakarta生态系统,旨在将社区从所有与javax相关的头痛中解放出来。

我使用IntelliJ,因为我最关心的是IntelliJ标记可能产生NPE的东西。我同意在JDK中没有标准注释是令人沮丧的。有传言说要添加它,它可能会加入到Java 7中。这样的话,就多了一个选择!

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

在最新的JSR-308草案的附录中有一些关于JSR-305状态的信息。这包括观察到JSR-305注释似乎被抛弃了。JSR-305页面还将其显示为“inactive”。

同时,实用的答案是使用最广泛使用的工具所支持的注释类型……如果情况发生变化,也要做好改变的准备。


事实上,JSR-308没有定义任何注释类型/类,而且看起来他们认为这超出了范围。(考虑到JSR-305的存在,他们是正确的)。

然而,如果JSR-308真的想要加入Java 8,那么如果人们对JSR-305重新产生兴趣,我也不会感到惊讶。AFAIK, JSR-305团队还没有正式放弃他们的工作。他们已经沉默了2年多了。

有趣的是,Bill Pugh (JSR-305的技术主管)是FindBugs背后的人之一。

One of the nice things about IntelliJ is that you don't need to use their annotations. You can write your own, or you can use those of whatever other tool you like. You're not even limited to a single type. If you're using two libraries that use different @NotNull annotations, you can tell IntelliJ to use both of them. To do this, go to "Configure Inspections", click on the "Constant Conditions & Exceptions" inspection, and hit the "Configure inspections" button. I use the Nullness Checker wherever I can, so I set up IntelliJ to use those annotations, but you can make it work with whatever other tool you want. (I have no opinion on the other tools because I've been using IntelliJ's inspections for years, and I love them.)