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


当前回答

如果你在做一个大项目,你最好创建自己的@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 
{
}

如果您使用了正确的保留策略,那么注释在运行时将不可用。从这个角度来看,它只是一种内在的东西。

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

这是一个内在的东西。(没有功能或技术影响) 有很多很多的用法。 像IntelliJ这样的IDE支持自定义的@Nullable/@NotNull注释。 大多数框架也喜欢使用自己的内部版本。

其他问题(见评论):

如何在IntelliJ中配置这个?

点击IntelliJ状态栏右下角的“警官”。在弹出窗口中单击“配置巡检”。下一个……

其他回答

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

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

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

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage有关于设置的建议。然而,如果你的工作空间中有外部项目(如facebook SDK),它们可能不满足这些建议,你可能不想在每次SDK更新时修复它们;-)

我使用:

空指针访问:错误 违反空规范:错误(链接到第1点) 潜在的空指针访问:警告(否则facebook SDK将有警告) 空注释和空推断之间的冲突:警告(链接到第3点)

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

对于不应该为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 8中还有另一种方法可以做到这一点。 我正在做两件事来完成我所需要的:

通过使用java.util.Optional包装可空字段,使可空字段显式地使用类型 在构造时使用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个构造函数:

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

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

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

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

我非常喜欢检查器框架,它是类型注释(JSR-308)的实现,用于实现像null检查器这样的缺陷检查器。我并没有尝试其他任何方法进行比较,但我对这个实现感到满意。

我没有加入提供该软件的组织,但我是它的粉丝。

我喜欢这个系统的四个方面:

It has a defect checkers for nullness (@Nullable), but also has ones for immutability and interning (and others). I use the first one (nullness) and I'm trying to get into using the second one (immutability/IGJ). I'm trying out the third one, but I'm not certain about using it long term yet. I'm not convinced of the general usefulness of the other checkers yet, but its nice to know that the framework itself is a system for implementing a variety of additional annotations and checkers. The default setting for nullness checking works well: Non-null except locals (NNEL). Basically this means that by default the checker treats everyhing (instance variables, method parameters, generic types, etc) except local variables as if they have a @NonNull type by default. Per the documentation: The NNEL default leads to the smallest number of explicit annotations in your code. You can set a different default for a class or for a method if NNEL doesn't work for you. This framework allows you to use with without creating a dependency on the framework by enclosing your annotations in a comment: e.g. /*@Nullable*/. This is nice because you can annotate and check a library or shared code, but still be able to use that library/shared coded in another project that doesn't use the framework. This is a nice feature. I've grown accustom to using it, even though I tend to enable the Checker Framework on all my projects now. The framework has a way to annotate APIs you use that aren't already annotated for nullness by using stub files.