Dal momento che JSR 305 (il cui obiettivo era standardizzare @NonNulle @Nullable) è rimasto inattivo per diversi anni, temo che non ci sia una buona risposta. Tutto quello che possiamo fare è trovare una soluzione pragmatica e la mia è la seguente:
Sintassi
Da un punto di vista puramente stilistico, vorrei evitare qualsiasi riferimento a IDE, framework o toolkit tranne Java stesso.
Questo esclude:
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
org.checkerframework.checker.nullness.qual
lombok.NonNull
Che ci lascia con javax.validation.constraintso javax.annotation. Il primo viene fornito con JEE. Se questo è meglio di javax.annotation, che potrebbe venire alla fine con JSE o mai del tutto, è una questione di dibattito. Personalmente preferisco javax.annotationperché non mi piacerebbe la dipendenza JEE.
Questo ci lascia con
javax.annotation
che è anche il più corto.
Esiste una sola sintassi che sarebbe ancora meglio: java.annotation.Nullable. Man mano che altri pacchetti passavano da javaxa javain passato, javax.annotation sarebbe un passo nella giusta direzione.
Implementazione
Speravo che tutti abbiano sostanzialmente la stessa banale implementazione, ma un'analisi dettagliata ha dimostrato che ciò non è vero.
Innanzitutto per le somiglianze:
Le @NonNullannotazioni hanno tutte la linea
public @interface NonNull {}
eccetto per
org.jetbrains.annotationsche lo chiama @NotNulle ha un'implementazione banale
javax.annotation che ha un'implementazione più lunga
javax.validation.constraintsche lo chiama anche @NotNulle ha un'implementazione
Le @Nullableannotazioni hanno tutte la linea
public @interface Nullable {}
fatta eccezione per (di nuovo) i org.jetbrains.annotationscon la loro banale implementazione.
Per le differenze:
Quello che colpisce è quello
javax.annotation
javax.validation.constraints
org.checkerframework.checker.nullness.qual
tutti hanno annotazioni di runtime ( @Retention(RUNTIME)), mentre
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
sono solo tempi di compilazione ( @Retention(CLASS)).
Come descritto in questa risposta SO, l'impatto delle annotazioni di runtime è minore di quanto si possa pensare, ma hanno il vantaggio di consentire agli strumenti di eseguire controlli di runtime oltre a quelli di compilazione.
Un'altra differenza importante è dove nel codice possono essere usate le annotazioni. Esistono due approcci diversi. Alcuni pacchetti usano contesti di stile JLS 9.6.4.1. La tabella seguente offre una panoramica:
PARAMETRO METODO DI CAMPO 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.annotationE org.checkerframework.checker.nullness.qualutilizzare i contesti definiti JLS 4.11, che è a mio parere il modo giusto per farlo.
Questo ci lascia con
javax.annotation
org.checkerframework.checker.nullness.qual
in questo round.
Codice
Per aiutarti a confrontare ulteriori dettagli da solo, elencherò il codice di ogni annotazione qui sotto. Per facilitare il confronto ho rimosso commenti, importazioni e @Documentedannotazioni. (avevano tutti @Documentedtranne le lezioni dal pacchetto Android). Ho riordinato le linee e i @Targetcampi e normalizzato le qualifiche.
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 {}
Per completezza, ecco le @Nullableimplementazioni:
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 {}
I seguenti due pacchetti non hanno @Nullable, quindi li elenco separatamente; Lombok ha un aspetto piuttosto noioso @NonNull. In javax.validation.constraintsthe @NonNullis a @NotNull
e ha un'implementazione longish.
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();
}
}
Supporto
Dalla mia esperienza, javax.annotationè almeno supportato da Eclipse e Checker Framework immediatamente.
Sommario
La mia annotazione ideale sarebbe la java.annotationsintassi con l'implementazione di Checker Framework.
Se non si intende utilizzare il Checker Framework javax.annotation( JSR-305 ) è ancora la soluzione migliore per il momento.
Se sei disposto ad acquistare nel Checker Framework usa semplicemente il loro org.checkerframework.checker.nullness.qual.
fonti
android.support.annotation a partire dal android-5.1.1_r1.jar
edu.umd.cs.findbugs.annotations a partire dal findbugs-annotations-1.0.0.jar
org.eclipse.jdt.annotation a partire dal org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
org.jetbrains.annotations a partire dal jetbrains-annotations-13.0.jar
javax.annotation a partire dal gwt-dev-2.5.1-sources.jar
org.checkerframework.checker.nullness.qual a partire dal checker-framework-2.1.9.zip
lombok a partire dal lombok commitf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints a partire dal validation-api-1.0.0.GA-sources.jar