由于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)
)。
就如同这个SO回答所描述的,运行时注解的影响要比想象中小,但是它们带来的好处是除了编译时检查之外能够让工具进行运行时检查。
另一个重要的区别是代码中可以使用注解的位置。有两种不同的方法。一些包使用JLS 9.6.4.1风格的上下文。以下表格给出了一个概述:
Package |
FIELD |
METHOD |
PARAMETER |
LOCAL_VARIABLE |
android.support.annotation |
✔️ |
✔️ |
✔️ |
|
edu.umd.cs.findbugs.annotations |
✔️ |
✔️ |
✔️ |
✔️ |
org.jetbrains.annotation |
✔️ |
✔️ |
✔️ |
✔️ |
lombok |
✔️ |
✔️ |
✔️ |
✔️ |
javax.validation.constraints |
✔️ |
✔️ |
✔️ |
|
org.eclipse.jdt.annotation
、javax.annotation
和org.checkerframework.checker.nullness.qual
使用JLS 4.11中定义的上下文,我认为这是正确的做法。
这让我们面临着
javax.annotation
org.checkerframework.checker.nullness.qual
在本轮中。
代码
为了帮助您自行比较更多细节,我列出了每个注释的代码。
为了更容易比较,我删除了注释、导入和@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()
@SubtypeOf(MonotonicNonNull.class)
@ImplicitFor(
types = ,
literals =
)
@DefaultQualifierInHierarchy
@DefaultFor()
@DefaultInUncheckedCodeFor()
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 中得到了原生支持。
摘要
我理想的注解应该是使用 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
com.sun.istack.internal.NotNull
。天哪... - Thomas Weller