Начиная с 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