Ponieważ JSR 305 (którego celem była standaryzacja @NonNulli @Nullable) był nieaktywny od kilku lat, obawiam się, że nie ma dobrej odpowiedzi. Wszystko, co możemy zrobić, to znaleźć pragmatyczne rozwiązanie, a moje jest następujące:
Składnia
Z czysto stylistycznego punktu widzenia chciałbym uniknąć jakiegokolwiek odniesienia do IDE, frameworka lub jakiegokolwiek zestawu narzędzi oprócz samego Java.
Wyklucza to:
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
org.checkerframework.checker.nullness.qual
lombok.NonNull
Który pozostawia nam albo javax.validation.constraintsalbo javax.annotation. Ten pierwszy pochodzi z JEE. Jeśli jest to lepsze niż javax.annotation, co może ostatecznie przynieść JSE lub wcale, jest kwestią do dyskusji. Osobiście wolę, javax.annotationponieważ nie chciałbym zależności JEE.
To nas zostawia
javax.annotation
który jest również najkrótszy.
Jest tylko jeden składnia co byłoby jeszcze lepiej: java.annotation.Nullable. Jak inne pakiety ukończył javaxaby javaw przeszłości javax.annotation byłby to krok we właściwym kierunku.
Realizacja
Miałem nadzieję, że wszystkie mają w zasadzie tę samą trywialną implementację, ale szczegółowa analiza wykazała, że to nieprawda.
Najpierw podobieństwa:
Wszystkie @NonNulladnotacje mają linię
public @interface NonNull {}
z wyjątkiem
org.jetbrains.annotationsktóry to nazywa @NotNulli ma trywialną implementację
javax.annotation który ma dłuższą implementację
javax.validation.constraintsktóry również to nazywa @NotNulli ma implementację
Wszystkie @Nullableadnotacje mają linię
public @interface Nullable {}
z wyjątkiem (ponownie) org.jetbrains.annotationsz ich trywialną implementacją.
Różnice:
Uderzające jest to
javax.annotation
javax.validation.constraints
org.checkerframework.checker.nullness.qual
wszystkie mają adnotacje środowiska wykonawczego ( @Retention(RUNTIME)), podczas gdy
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
są tylko czasem kompilacji ( @Retention(CLASS)).
Jak opisano w tej odpowiedzi SO, wpływ adnotacji środowiska wykonawczego jest mniejszy, niż mogłoby się wydawać, ale mają one tę zaletę, że oprócz narzędzi do kompilacji mają także możliwość przeprowadzania kontroli środowiska wykonawczego.
Inną ważną różnicą jest to, gdzie w kodzie można używać adnotacji. Istnieją dwa różne podejścia. Niektóre pakiety używają kontekstów w stylu JLS 9.6.4.1. Poniższa tabela zawiera przegląd:
PARAMETR METODY POLA LOCAL_VARIABLE
android.support.annotation XXX
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
lombok XXXX
javax.validation.constraints XXX
org.eclipse.jdt.annotation, javax.annotationI org.checkerframework.checker.nullness.qualużyć konteksty określonych w JLS 4.11, co jest moim zdaniem dobry sposób, żeby to zrobić.
To nas zostawia
javax.annotation
org.checkerframework.checker.nullness.qual
w tej rundzie.
Kod
Aby pomóc Ci porównać dalsze szczegóły, poniżej zamieszczam kod każdej adnotacji. Aby ułatwić porównywanie, usunąłem komentarze, import i @Documentedadnotacje. (wszyscy mieli @Documentedoprócz zajęć z pakietu Androida). Zmieniłem kolejność linii i @Targetpól oraz znormalizowałem kwalifikacje.
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 {}
Dla kompletności, oto @Nullableimplementacje:
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 {}
Poniższe dwa pakiety nie mają @Nullable, więc wymieniam je osobno; Lombok jest dość nudny @NonNull. W jest w rzeczywistości
i ma dość długi wdrożenia.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();
}
}
Wsparcie
Z mojego doświadczenia javax.annotationwynika , że jest co najmniej obsługiwany przez Eclipse i Checker Framework od razu po wyjęciu z pudełka.
Podsumowanie
Moją idealną adnotacją byłaby java.annotationskładnia z implementacją Checker Framework.
Jeśli nie zamierzasz korzystać z Checker Framework, javax.annotation( JSR-305 ) jest na razie najlepszym wyborem.
Jeśli chcesz kupić w Checker Framework, skorzystaj z nich org.checkerframework.checker.nullness.qual.
Źródła
android.support.annotation od android-5.1.1_r1.jar
edu.umd.cs.findbugs.annotations od findbugs-annotations-1.0.0.jar
org.eclipse.jdt.annotation od org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
org.jetbrains.annotations od jetbrains-annotations-13.0.jar
javax.annotation od gwt-dev-2.5.1-sources.jar
org.checkerframework.checker.nullness.qual od checker-framework-2.1.9.zip
lombokz lombokzatwierdzeniaf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints od validation-api-1.0.0.GA-sources.jar