由于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 并有一个实现
@Nullableannotations 都有一行
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_VARIABLE
android.support.annotation X X X
edu.umd.cs.findbugs.annotations X X X X
org.jetbrains.annotation X X X X
龙目岛 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
在这一轮中。
代码
为了帮助您自己比较更多细节,我在下面列出了每个注释的代码。
为了便于比较,我删除了 cmets、imports 和 @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,所以我单独列出来;龙目岛有一个很无聊的@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 框架,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 来自 lombok 提交 f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
-
javax.validation.constraints 来自validation-api-1.0.0.GA-sources.jar