Оскільки 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