Dal momento che JSR 305 (il cui obiettivo era standardizzare @NonNull
e @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.constraints
o 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.annotation
perché 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 javax
a java
in 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 @NonNull
annotazioni hanno tutte la linea
public @interface NonNull {}
eccetto per
org.jetbrains.annotations
che lo chiama @NotNull
e ha un'implementazione banale
javax.annotation
che ha un'implementazione più lunga
javax.validation.constraints
che lo chiama anche @NotNull
e ha un'implementazione
Le @Nullable
annotazioni hanno tutte la linea
public @interface Nullable {}
fatta eccezione per (di nuovo) i org.jetbrains.annotations
con 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.annotation
E org.checkerframework.checker.nullness.qual
utilizzare 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 @Documented
annotazioni. (avevano tutti @Documented
tranne le lezioni dal pacchetto Android). Ho riordinato le linee e i @Target
campi 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 @Nullable
implementazioni:
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.constraints
the @NonNull
is 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.annotation
sintassi 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