Поскольку Oracle решил не стандартизировать @NonNull (и @Nullable) на данный момент, я боюсь, что нет хорошего ответа.Все, что мы можем сделать, это найти прагматическое решение, и мое следующее:
Синтаксис
С чисто стилистической точки зрения я хотел бы избежать любых ссылок на IDE, Framework или любой инструментарий, кроме 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.В следующей таблице представлен обзор:
FIELD METHOD PARAMETER LOCAL_VARIABLE
android.support.annotation X X X
edu.umd.cs.findbugs.annotations X X X X
org.jetbrains.annotation X X X X
lombok 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
в этом раунде.
Код
Чтобы помочь вам самостоятельно сравнить дальнейшие детали, я перечислю код каждой аннотации ниже.Чтобы упростить сравнение, я удалил комментарии, импорт и аннотацию @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 from findbugs-annotations-1.0.0.jar
- org.eclipse.jdt.annotation от org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
- org.jetbrains.nnotations из 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 commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
- javax.validation.constraints из validation-api-1.0.0.GA-sources.jar