我应该使用哪个@NotNull Java注释?
我期望让代码更具可读性,并使用IDE代码检查和/或静态代码分析(FindBugs和Sonar)等工具来避免NullPointerException。 许多工具似乎与其他人的@NotNull
/ @NonNull
/ @Nonnull
注解不兼容,并且在我的代码中列出所有这些工具会非常糟糕。 任何建议哪一个是'最好的'? 以下是我找到的等效注释列表:
javax.validation.constraints.NotNull
创建用于运行时验证,而不是静态分析。
文件
edu.umd.cs.findbugs.annotations.NonNull
用于Findbugs静态分析,因此Sonar(现在是Sonarqube)
文件
javax.annotation.Nonnull
这可能也适用于Findbugs,但JSR-305处于非活动状态。 (另请参阅:JSR 305的状态是什么?)来源
org.jetbrains.annotations.NotNull
IntelliJ IDEA IDE用于静态分析。
文件
lombok.NonNull
用于控制Project Lombok中的代码生成。
由于没有标准,占位符注释。
来源,文件
android.support.annotation.NonNull
Android中提供的标记注释,由支持注释包提供
文件
org.eclipse.jdt.annotation.NonNull
Eclipse用于静态代码分析
文件
我非常喜欢Checker框架,它是一个类型注释(JSR-308)的实现,它用于实现像nullness检查器这样的缺陷检查器。 我没有真正尝试过其他人提供任何比较,但我对此实施感到满意。
我不提供该软件的团队,但我是一个粉丝。
我喜欢这个系统的四件事情:
它有一个用于无效的缺陷检查器(@Nullable),但也有用于不可变性和实习(以及其他)的缺陷检查器。 我使用第一个(nullness),我试图使用第二个(不变性/ IGJ)。 我正在尝试第三种,但我不确定如何长期使用它。 我并不确信其他检查工具的一般用处,但很高兴知道框架本身就是一个实现各种附加注释和检查工具的系统。
空值检查的默认设置工作正常:非本地除外(非本地)(NNEL)。 基本上这意味着默认情况下,检查器会将除局部变量以外的所有元素(实例变量,方法参数,泛型类型等)视为缺省为@NonNull类型。 根据文档:
NNEL默认会导致代码中显式注释的数量最少。
如果NNEL不适合您,可以为类或方法设置不同的默认值。
这个框架允许你在没有创建对框架的依赖的情况下使用, 把你的注释封装在注释中:例如/*@Nullable*/
。 这很好,因为您可以注释并检查库或共享代码,但仍可以在不使用框架的另一个项目中使用该库/共享编码。 这是一个很好的功能。 我已经习惯使用它了,尽管我现在倾向于在我所有的项目中启用Checker Framework。
该框架有一种方法可以通过使用存根文件来注释您使用的API尚未注释为空。
我使用IntelliJ,因为我主要关注IntelliJ标记可能产生NPE的事情。 我同意在JDK中没有标准注释让人感到沮丧。 有关于添加它的讨论,它可能使它成为Java 7.在这种情况下,还会有更多选择!
由于甲骨文决定暂时不对@NonNull(和@Nullable)进行标准化,恐怕没有好的答案。 我们所能做的就是找到一个务实的解决方案,我的方法如下:
句法
从纯文体的角度来看,我希望避免提及IDE,框架或除Java本身之外的任何工具包。
这排除了:
这使我们无论是使用javax.validation.constraints还是javax.annotation。 前者带有JEE。 如果这比javax.annotation更好,最终可能会发生在JSE上,或者根本不会发生,这是一个争论。 我个人更喜欢javax.annotation,因为我不喜欢JEE依赖。
这给我们留下了
javax.annotation中
这也是最短的一个。
只有一种语法甚至更好:java.annotation.Nullable。 由于其他包从javax到java的过去都是从java毕业的,所以javax.annotation将朝着正确的方向迈出一大步。
履行
我希望他们都有基本相同的微不足道的实施,但详细的分析表明这不是事实。
首先是相似之处:
@NonNull注释都有这一行
public @interface NonNull {}
除了
@Nullable注释都有行
public @interface Nullable {}
除了(再次)org.jetbrains.annotations以及它们微不足道的实现之外。
对于差异:
一个引人注目的是
都有运行时注释(@Retention(RUNTIME),while
只有编译时间(@Retention(CLASS))。
正如本文中所述,运行时注释的影响比人们想象的要小,但是除了编译时间之外,它们还具有启用工具来执行运行时检查的好处。
另一个重要的区别是代码中可以使用注释的地方。 有两种不同的方法。 有些软件包使用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中定义的上下文,这在我看来是正确的。
这给我们留下了
在这一轮。
码
为了帮助您自己比较进一步的细节,我列出了下面每个注释的代码。 为了便于比较,我删除了注释,导入和@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.annotation至少受Eclipse和Checker Framework的支持。
概要
我的理想注解是Checker框架实现的java.annotation语法。
如果您不打算使用Checker框架,那么javax.annotation (JSR-305)暂时仍然是您最好的选择。
如果你愿意购买Checker框架,只需使用他们的org.checkerframework.checker.nullness.qual。
来源
上一篇: Which @NotNull Java annotation should I use?
下一篇: Remove last comma from JSON constructed with foreach loop