Оскільки 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)).
Як описано в цій відповіді на відповідь, вплив анотацій під час виконання є меншим, ніж можна було б подумати, але вони мають перевагу в тому, що вони дозволяють інструментам робити перевірки виконання на додаток до даних про час компіляції.
Ще одна важлива відмінність полягає в тому, де в коді можна використовувати анотації. Є два різні підходи. Деякі пакети використовують контексти стилів JLS 9.6.4.1. Наступна таблиця дає огляд:
ПАРАМЕТР ПОЛЬНОГО МЕТОДУ LOCAL_VARIABLE
android.support.annotation XXX
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
ломбок 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