我应该使用哪个 @NotNull Java 注释? [英] Which @NotNull Java annotation should I use?
问题描述
我希望让我的代码更具可读性,并使用 IDE 代码检查和/或静态代码分析(FindBugs 和 Sonar)等工具来避免 NullPointerExceptions.许多工具似乎与彼此的 @NotNull
/@NonNull
/@Nonnull
注释不兼容,并在我的代码中列出所有这些工具可怕的阅读.任何建议哪个是最好的"?以下是我找到的等效注释列表:
javax.validation.constraints.NotNull
创建用于运行时验证,而非静态分析.
文档edu.umd.cs.findbugs.annotations.NonNull
由 FindBugs 使用(死项目) 及其继任者 SpotBugs 静态分析和 Sonar(现在 Sonarqube)
FindBugs 文档、SpotBugs 文档javax.annotation.Nonnull
这可能也适用于 FindBugs,但 JSR-305 处于非活动状态.(另请参阅:JSR 305 的状态是什么?)源码org.jetbrains.annotations.NotNull
IntelliJ IDEA IDE 用于静态分析.
文档lombok.NonNull
用于控制 Project Lombok 中的代码生成.
占位符注释,因为没有标准.
来源,文档androidx.annotation.NonNull
Android 中可用的标记注解,由注解包提供
文档org.eclipse.jdt.annotation.NonNull
Eclipse 用于静态代码分析
文档>
自从 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 样式上下文.下表提供了概览:
<前>现场方法参数 LOCAL_VARIABLEandroid.support.annotation X X Xedu.umd.cs.findbugs.annotations X X X Xorg.jetbrains.annotation X X X X龙目岛 X X X Xjavax.validation.constraints X X Xorg.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;@保留(类)@目标({字段,方法,参数})公共@interface NonNull {}
<小时>
包edu.umd.cs.findbugs.annotations;@保留(类)@目标({字段,方法,参数,本地变量})公共@interface NonNull {}
<小时>
package org.eclipse.jdt.annotation;@保留(类)@Target({ TYPE_USE })公共@interface NonNull {}
<小时>
package org.jetbrains.annotations;@保留(类)@目标({字段,方法,参数,本地变量})public @interface NotNull {String value() 默认"";}
<小时>
package javax.annotation;@TypeQualifier@保留(运行时间)公共@interface 非空{当 when() 默认 When.ALWAYS;静态类 Checker 实现 TypeQualifierValidator{public 当 forConstantValue(Nonnull qualifierqualifierArgument,对象值) {如果(值 == 空)返回 When.NEVER;返回 When.ALWAYS;}}}
<小时>
package org.checkerframework.checker.nullness.qual;@保留(运行时间)@Target({TYPE_USE, TYPE_PARAMETER})@SubtypeOf(MonotonicNonNull.class)@ImplicitFor(类型 = {TypeKind.PACKAGE,TypeKind.INT,TypeKind.BOOLEAN,TypeKind.CHAR,TypeKind.DOUBLE,TypeKind.FLOAT,类型Kind.LONG,TypeKind.SHORT,类型Kind.BYTE},文字 = {LiteralKind.STRING})@DefaultQualifierInHierarchy@DefaultFor({TypeUseLocation.EXCEPTION_PARAMETER})@DefaultInUncheckedCodeFor({TypeUseLocation.PARAMETER, TypeUseLocation.LOWER_BOUND})公共@interface NonNull {}
<小时>
为了完整起见,这里是 @Nullable
实现:
package android.support.annotation;@保留(类)@Target({方法,参数,字段})公共 @interface 可为空 {}
<小时>
包edu.umd.cs.findbugs.annotations;@目标({字段,方法,参数,本地变量})@保留(类)公共 @interface 可为空 {}
<小时>
package org.eclipse.jdt.annotation;@保留(类)@Target({ TYPE_USE })公共 @interface 可为空 {}
<小时>
package org.jetbrains.annotations;@保留(类)@目标({字段,方法,参数,本地变量})public @interface Nullable {String value() default "";}
<小时>
package javax.annotation;@TypeQualifierNickname@Nonnull(when = When.UNKNOWN)@保留(运行时间)公共 @interface 可为空 {}
<小时>
package org.checkerframework.checker.nullness.qual;@保留(运行时间)@Target({TYPE_USE, TYPE_PARAMETER})@SubtypeOf({})@ImplicitFor(文字 = {LiteralKind.NULL},typeNames = {java.lang.Void.class})@DefaultInUncheckedCodeFor({TypeUseLocation.RETURN, TypeUseLocation.UPPER_BOUND})公共 @interface 可为空 {}
下面两个包没有@Nullable
,所以我单独列出来;Lombok 有一个很无聊的@NonNull
.在 javax.validation.constraints
中,@NonNull
实际上是一个 @NotNull
它的实现时间很长.
包 lombok;@保留(类)@目标({字段,方法,参数,本地变量})公共@interface NonNull {}
<小时>
package javax.validation.constraints;@保留(运行时间)@Target({ 字段,方法,注释类型,构造函数,参数})@Constraint(validatedBy = {})公共@interface NotNull {String message() 默认{javax.validation.constraints.NotNull.message}";Class<?>[] groups() default { };类[] payload() 默认{};@目标({方法,字段,注释类型,构造函数,参数})@保留(运行时间)@记录@接口列表{NotNull[] value();}}
<小时>
支持
根据我的经验,javax.annotation
至少得到 Eclipse 和开箱即用的 Checker 框架的支持.
总结
我理想的注释是带有 Checker 框架实现的 java.annotation
语法.
如果您不打算使用 Checker 框架,javax.annotation
(JSR-305) 仍然是您目前最好的选择.
如果您愿意购买 Checker 框架,请使用他们的 org.checkerframework.checker.nullness.qual
.
来源
android.support.annotation
来自android-5.1.1_r1.jar
edu.umd.cs.findbugs.annotations
来自findbugs-annotations-1.0.0.jar
org.eclipse.jdt.annotation
来自org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
org.jetbrains.annotations
来自jetbrains-annotations-13.0.jar
javax.annotation
来自gwt-dev-2.5.1-sources.jar
org.checkerframework.checker.nullness.qual
来自checker-framework-2.1.9.zip
lombok
fromlombok
commitf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints
来自validation-api-1.0.0.GA-sources.jar
I'm looking to make my code more readable as well as use tooling like IDE code inspection and/or static code analysis (FindBugs and Sonar) to avoid NullPointerExceptions. Many of the tools seem incompatible with each others' @NotNull
/@NonNull
/@Nonnull
annotation and listing all of them in my code would be terrible to read. Any suggestions of which one is the 'best'? Here is the list of equivalent annotations I've found:
javax.validation.constraints.NotNull
Created for runtime validation, not static analysis.
documentationedu.umd.cs.findbugs.annotations.NonNull
Used by FindBugs (dead project) and its successor SpotBugs static analysis and therefore Sonar (now Sonarqube)
FindBugs documentation, SpotBugs documentationjavax.annotation.Nonnull
This might work with FindBugs too, but JSR-305 is inactive. (See also: What is the status of JSR 305?) sourceorg.jetbrains.annotations.NotNull
Used by IntelliJ IDEA IDE for static analysis.
documentationlombok.NonNull
Used to control code generation in Project Lombok.
Placeholder annotation since there is no standard.
source, documentationandroidx.annotation.NonNull
Marker annotation available in Android, provided by annotation package
documentationorg.eclipse.jdt.annotation.NonNull
Used by Eclipse for static code analysis
documentation
Since JSR 305 (whose goal was to standardize @NonNull
and @Nullable
) has been dormant for several years, I'm afraid there is no good answer. All we can do is to find a pragmatic solution and mine is as follows:
Syntax
From a purely stylistic standpoint I would like to avoid any reference to IDE, framework or any toolkit except Java itself.
This rules out:
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
org.checkerframework.checker.nullness.qual
lombok.NonNull
Which leaves us with either javax.validation.constraints
or javax.annotation
.
The former comes with JEE. If this is better than javax.annotation
, which might come eventually with JSE or never at all, is a matter of debate.
I personally prefer javax.annotation
because I wouldn't like the JEE dependency.
This leaves us with
javax.annotation
which is also the shortest one.
There is only one syntax which would even be better: java.annotation.Nullable
. As other packages graduated
from javax
to java
in the past, the javax.annotation would
be a step in the right direction.
Implementation
I was hoping that they all have basically the same trivial implementation, but a detailed analysis showed that this is not true.
First for the similarities:
The @NonNull
annotations all have the line
public @interface NonNull {}
except for
org.jetbrains.annotations
which calls it@NotNull
and has a trivial implementationjavax.annotation
which has a longer implementationjavax.validation.constraints
which also calls it@NotNull
and has an implementation
The @Nullable
annotations all have the line
public @interface Nullable {}
except for (again) the org.jetbrains.annotations
with their trivial implementation.
For the differences:
A striking one is that
javax.annotation
javax.validation.constraints
org.checkerframework.checker.nullness.qual
all have runtime annotations (@Retention(RUNTIME)
), while
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
are only compile time (@Retention(CLASS)
).
As described in this SO answer the impact of runtime annotations is smaller than one might think, but they have the benefit of enabling tools to do runtime checks in addition to the compile time ones.
Another important difference is where in the code the annotations can be used. There are two different approaches. Some packages use JLS 9.6.4.1 style contexts. The following table gives an overview:
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
and org.checkerframework.checker.nullness.qual
use the contexts defined in
JLS 4.11, which is in my opinion the right way to do it.
This leaves us with
javax.annotation
org.checkerframework.checker.nullness.qual
in this round.
Code
To help you to compare further details yourself I list the code of every annotation below.
To make comparison easier I removed comments, imports and the @Documented
annotation.
(they all had @Documented
except for the classes from the Android package).
I reordered the lines and @Target
fields and normalized the qualifications.
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 {}
For completeness, here are the @Nullable
implementations:
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 {}
The following two packages have no @Nullable
, so I list them separately; Lombok has a pretty boring @NonNull
.
In javax.validation.constraints
the @NonNull
is actually a @NotNull
and it has a longish implementation.
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();
}
}
Support
From my experience, javax.annotation
is at least supported by Eclipse and the Checker Framework out of the box.
Summary
My ideal annotation would be the java.annotation
syntax with the Checker Framework implementation.
If you don't intend to use the Checker Framework the javax.annotation
(JSR-305) is still your best bet for the time being.
If you are willing to buy into the Checker Framework just use
their org.checkerframework.checker.nullness.qual
.
Sources
android.support.annotation
fromandroid-5.1.1_r1.jar
edu.umd.cs.findbugs.annotations
fromfindbugs-annotations-1.0.0.jar
org.eclipse.jdt.annotation
fromorg.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
org.jetbrains.annotations
fromjetbrains-annotations-13.0.jar
javax.annotation
fromgwt-dev-2.5.1-sources.jar
org.checkerframework.checker.nullness.qual
fromchecker-framework-2.1.9.zip
lombok
fromlombok
commitf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints
fromvalidation-api-1.0.0.GA-sources.jar
这篇关于我应该使用哪个 @NotNull Java 注释?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!