Начиная с 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. Следующая таблица дает обзор:
ПАРАМЕТР ПОЛЯ МЕТОДА LOCAL_VARIABLE
android.support.annotation XXX
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation 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 {}
Следующие два пакета не имеют @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 из коробки.
Резюме
Моей идеальной аннотацией был бы 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