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

我希望使我的代码更具可读性,并使用IDE代码检查和/或静态代码分析(FindBugs和Sonar)等工具来避免NullPointerExceptions.许多工具似乎与彼此不兼容的@NotNull / @NonNull / @Nonnull注释并列出我的代码中的所有这些工具都很难阅读.有什么建议是“最好的”吗?这是我发现的等效注释列表:

> javax.validation.constraints.NotNull
创建用于运行时验证,而不是静态分析.
documentation
> edu.umd.cs.findbugs.annotations.NonNull
Findbugs静态分析使用,因此声纳(现在Sonarqube)
documentation
> javax.annotation.Nonnull
这可能也适用于Findbugs,但JSR-305处于非活动状态. (另见:What is the status of JSR 305?)
source
> org.jetbrains.annotations.NotNull
由IntelliJ IDEA IDE用于静态分析.
documentation
> lombok.NonNull
用于控制Project Lombok中的代码生成.
占位符注释,因为没有标准.
source,
documentation
> android.support.annotation.NonNull
Android中提供的标记注释,由support-annotations包提供
documentation
> org.eclipse.jdt.annotation.NonNull
由Eclipse用于静态代码分析
documentation

解决方法:

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.NonNull

这给我们留下了javax.validation.constraints或javax.annotation.
前者配有JEE.如果这比javax.annotation更好,这可能最终会出现在JSE中,或者从来没有出现过,这是一个有争议的问题.
我个人更喜欢javax.annotation,因为我不喜欢JEE依赖.

这让我们失望了

javax.annotation中

这也是最短的一个.

只有一种语法更好:java.annotation.Nullable.随着其他包的毕业
从javax到java过去,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)).

this SO answer所述,运行时注释的影响
比人们想象的要小,但它们有好处
启用运行时检查的工具除了
编译时间.

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


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

org.eclipse.jdt.annotation,javax.annotation和org.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 {}

以下两个包没有@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.annotation至少得到Eclipse和Checker Framework的支持.

摘要

我理想的注释是带有Checker Framework实现的java.annotation语法.

如果您不打算使用Checker Framework,那么javax.annotation(JSR-305)仍然是您最好的选择.

如果您愿意购买Checker Framework,请使用
他们的org.checkerframework.checker.nullness.qual.

来源

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

上一篇:java – 将null返回为int三元运算符允许的int,而不是if语句


下一篇:java – 在空对象引用上调用虚方法’double android.location.Location.getLatitude()’时出错