自从JSR 305(其目标是标准化@NonNull
和@Nullable
)已经休眠了好几年以来,恐怕没有好的答案。我们所能做的就是找到一个务实的解决方案,我的方法如下:
句法
从纯粹的风格角度来看,除了Java本身,我想避免引用任何IDE,框架或任何工具包。
这排除了:
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 更好,那么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)
)。
如本SO所述,运行时批注的影响比人们想象的要小,但是它们的好处是使工具能够在编译时进行运行时检查。
另一个重要的区别是注释可以在代码中的何处使用。有两种不同的方法。一些软件包使用JLS 9.6.4.1样式上下文。下表概述了:
字段方法参数LOCAL_VARIABLE
android.support.annotation XXX
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
龙目岛XXXX
javax.validation.constraints XXX
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 {}
以下两个软件包没有no @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至少是开箱即用的。
摘要
我理想的注释是java.annotation
Checker Framework实现的语法。
如果您不打算使用Checker Framework,那么javax.annotation
(JSR-305)仍然是目前最好的选择。
如果您愿意购买Checker Framework,请使用它们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
从lombok
提交f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints
从 validation-api-1.0.0.GA-sources.jar