Dado que JSR 305 (cuyo objetivo era estandarizar @NonNully @Nullable) ha estado inactivo durante varios años, me temo que no hay una buena respuesta. Todo lo que podemos hacer es encontrar una solución pragmática y la mía es la siguiente:
Sintaxis
Desde un punto de vista puramente estilístico, me gustaría evitar cualquier referencia a IDE, framework o cualquier kit de herramientas, excepto Java.
Esto descarta:
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
org.checkerframework.checker.nullness.qual
lombok.NonNull
Lo que nos deja con javax.validation.constraintso javax.annotation. El primero viene con JEE. Si esto es mejor que javax.annotation, lo que podría ocurrir con JSE o nunca, es un tema de debate. Personalmente prefiero javax.annotationporque no me gustaría la dependencia JEE.
Esto nos deja con
javax.annotation
que es también el más corto
Sólo hay una sintaxis que incluso puede ser mejor: java.annotation.Nullable. A medida que otros paquetes se graduaron de javaxa javaen el pasado, la anotación javax sería un paso en la dirección correcta.
Implementación
Esperaba que todos tuvieran básicamente la misma implementación trivial, pero un análisis detallado mostró que esto no es cierto.
Primero por las similitudes:
Las @NonNullanotaciones todos tienen la línea
public @interface NonNull {}
excepto por
org.jetbrains.annotationsque lo llama @NotNully tiene una implementación trivial
javax.annotation que tiene una implementación más larga
javax.validation.constraintsque también lo llama @NotNully tiene una implementación
Las @Nullableanotaciones todos tienen la línea
public @interface Nullable {}
a excepción de (nuevamente) org.jetbrains.annotationscon su implementación trivial.
Por las diferencias:
Una sorprendente es que
javax.annotation
javax.validation.constraints
org.checkerframework.checker.nullness.qual
todos tienen anotaciones de tiempo de ejecución ( @Retention(RUNTIME)), mientras que
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
son solo tiempo de compilación ( @Retention(CLASS)).
Como se describe en esta respuesta SO, el impacto de las anotaciones de tiempo de ejecución es menor de lo que uno podría pensar, pero tienen el beneficio de permitir que las herramientas realicen comprobaciones de tiempo de ejecución además de las de tiempo de compilación.
Otra diferencia importante es dónde se pueden usar las anotaciones en el código. Hay dos enfoques diferentes. Algunos paquetes usan contextos de estilo JLS 9.6.4.1. La siguiente tabla ofrece una descripción general:
MÉTODO DE CAMPO PARÁMETRO VARIABLE LOCAL
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.annotationy org.checkerframework.checker.nullness.qualuse los contextos definidos en JLS 4.11, que en mi opinión es la forma correcta de hacerlo.
Esto nos deja con
javax.annotation
org.checkerframework.checker.nullness.qual
en esta ronda
Código
Para ayudarlo a comparar más detalles usted mismo, enumero el código de cada anotación a continuación. Para facilitar la comparación, eliminé los comentarios, las importaciones y la @Documentedanotación. (todos tenían a @Documentedexcepción de las clases del paquete de Android). Reordené las líneas y los @Targetcampos y normalicé las calificaciones.
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 {}
Para completar, aquí están las @Nullableimplementaciones:
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 {}
Los siguientes dos paquetes no tienen @Nullable, así que los enumero por separado; Lombok tiene una muy aburrida @NonNull. En javax.validation.constraintsla @NonNullrealidad es una @NotNull
y tiene una aplicación bastante largo.
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();
}
}
Apoyo
Desde mi experiencia, javax.annotational menos es compatible con Eclipse y el Checker Framework listo para usar.
Resumen
Mi anotación ideal sería la java.annotationsintaxis con la implementación de Checker Framework.
Si no tiene la intención de utilizar el Checker Framework, el javax.annotation( JSR-305 ) sigue siendo su mejor apuesta por el momento.
Si está dispuesto a comprar el Checker Framework, simplemente use su org.checkerframework.checker.nullness.qual.
Fuentes
android.support.annotation desde android-5.1.1_r1.jar
edu.umd.cs.findbugs.annotations desde findbugs-annotations-1.0.0.jar
org.eclipse.jdt.annotation desde org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
org.jetbrains.annotations desde jetbrains-annotations-13.0.jar
javax.annotation desde gwt-dev-2.5.1-sources.jar
org.checkerframework.checker.nullness.qual desde checker-framework-2.1.9.zip
lombokde lombokcommitf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints desde validation-api-1.0.0.GA-sources.jar