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


当前回答

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

其他回答

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

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

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

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

我使用:

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

对于Android项目,您应该使用Android .support.annotation. nonnull和Android .support.annotation. nullable。这些和其他有用的特定于android的注释在支持库中可用。

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

支持库本身也使用这些注释 注解,所以作为支持库的用户,Android Studio将 检查您的代码并基于这些标记潜在的问题 注释。

在等待上游(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 {}

不可否认,这主要是为了装饰或未来的目的,因为上面显然没有为这些注释的静态分析增加任何支持。

如果你正在使用Spring框架构建应用程序,我建议使用来自bean Validation的javax.validation.constraints.NotNull,它被打包在以下依赖项中:

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

这种注释的主要优点是Spring同时支持用javax.validation.constraints.NotNull注释的方法参数和类字段。要启用支持,您需要做的是:

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> provide MethodValidationPostProcessor to spring's context @Configuration @ValidationConfig public class ValidationConfig implements MyService { @Bean public MethodValidationPostProcessor providePostProcessor() { return new MethodValidationPostProcessor() } } 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  
  }
}

当您尝试调用方法doSomething并传递null作为参数值时,spring(通过HibernateValidator)将抛出ConstraintViolationException。这里不需要手工工作。

您还可以验证返回值。

javax.validation.constraints.NotNull对Beans验证框架的另一个重要好处是,目前它仍在开发中,新特性计划在新版本2.0中实现。

@Nullable呢?在Beans Validation 1.1中没有类似的东西。好吧,我可以争辩说,如果你决定使用@ nonull,而不是任何没有用@NonNull注释的东西都是有效的“nullable”,所以@Nullable注释是无用的。

由于JSR 305(其目标是标准化@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。null

这使得我们只能使用javax.validation.constraints或javax.annotation。 前者随JEE一起提供。如果这比javax更好。注释可能最终随JSE一起出现,也可能永远不会出现,这是一个有争议的问题。 我个人更喜欢javax。注释,因为我不喜欢JEE依赖。

这就留给我们

javax.annotation

这也是最短的一个。

只有一种语法会更好:java.annotation.Nullable。随着其他课程的毕业 从javax到Java,过去的javax。注释会 向正确的方向迈出一步。

实现

我希望它们都有基本相同的琐碎实现, 但一项详细的分析表明,事实并非如此。

首先是相似之处:

@NonNull注释都有一行

public @interface NonNull {}

除了

它将其命名为@NotNull,并有一个简单的实现 javax。具有较长实现的注释 也叫@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回答了运行时注释的影响 比人们想象的要小,但他们有好处吗 使工具能够执行运行时检查的 编译时1。

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

Package FIELD METHOD PARAMETER LOCAL_VARIABLE
android.support.annotation ✔️ ✔️ ✔️
edu.umd.cs.findbugs.annotations ✔️ ✔️ ✔️ ✔️
org.jetbrains.annotation ✔️ ✔️ ✔️ ✔️
lombok ✔️ ✔️ ✔️ ✔️
javax.validation.constraints ✔️ ✔️ ✔️

org.eclipse.jdt。注释、javax。Annotation和org.checkerframework.checker. nullnness .qual使用中定义的上下文 JLS 4.11,在我看来这是正确的方法。

这就留给我们

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

在这一轮。

Code

为了帮助您自己进一步比较细节,我在下面列出了每个注释的代码。 为了便于比较,我删除了注释、导入和@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。注释至少被Eclipse和Checker框架开箱即用所支持。

总结

我的理想注释是java。注释语法与检查器框架实现。

如果你不打算使用检查器框架,javax。注释(JSR-305)仍然是目前最好的选择。

如果您愿意购买检查器框架,请使用 org.checkerframework.checker.nullness.qual。


来源

android.support.annotation from android-5.1.1_r1.jar edu.umd.cs.findbugs.annotations from findbugs-annotations-1.0.0.jar org.eclipse.jdt.annotation from org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar org.jetbrains.annotations from jetbrains-annotations-13.0.jar javax.annotation from gwt-dev-2.5.1-sources.jar org.checkerframework.checker.nullness.qual from checker-framework-2.1.9.zip lombok from lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4 javax.validation.constraints from validation-api-1.0.0.GA-sources.jar