Pregunta ¿Qué anotación de @NotNull Java debería usar?


Estoy buscando hacer que mi código sea más legible, así como usar herramientas como inspección de código IDE y / o análisis de código estático (FindBugs y Sonar) para evitar NullPointerExceptions. Muchas de las herramientas parecen incompatibles entre sí @NotNull/@NonNull/@Nonnull anotar y enumerar todos ellos en mi código sería terrible de leer. ¿Alguna sugerencia de cuál es el "mejor"? Aquí está la lista de anotaciones equivalentes que he encontrado:

  • javax.validation.constraints.NotNull
    Creado para validación en tiempo de ejecución, no análisis estático.
    documentación

  • edu.umd.cs.findbugs.annotations.NonNull
    Usado por Findbugs análisis estático y, por lo tanto, Sonar (ahora Sonarqube)
    documentación

  • javax.annotation.Nonnull
    Esto también podría funcionar con Findbugs, pero JSR-305 está inactivo (Ver también: ¿Cuál es el estado de JSR 305?) fuente

  • org.jetbrains.annotations.NotNull
    Usado por IntelliJ IDEA IDE para análisis estático.
    documentación

  • lombok.NonNull
    Se usa para controlar la generación de código en Proyecto Lombok.
    Anotación de marcador de posición ya que no hay un estándar.
    fuente, documentación

  • android.support.annotation.NonNull
    Anotación de marcador disponible en Android, proporcionada por el paquete de anotaciones de soporte
    documentación

  • org.eclipse.jdt.annotation.NonNull
    Utilizado por Eclipse para el análisis de código estático
    documentación


739
2018-02-10 22:05


origen


Respuestas:


Me gusta mucho el Checker Framework, que es una implementación de anotaciones tipo (JSR-308) que se utiliza para implementar verificadores de defectos como un verificador de nulidad. Realmente no he intentado con otros para ofrecer ninguna comparación, pero estoy contento con esta implementación.

No estoy afiliado al grupo que ofrece el software, pero soy un fanático.

Cuatro cosas que me gustan de este sistema:

  1. Tiene un defecto inspectores para nulidad (@Nullable), pero también tiene uno para inmutabilidad y pasantía (y otros). Utilizo el primero (nulidad) y trato de utilizar el segundo (inmutabilidad / IGJ). Estoy probando el tercero, pero aún no estoy seguro de usarlo a largo plazo. Todavía no estoy convencido de la utilidad general de las otras comprobaciones, pero es bueno saber que el marco en sí es un sistema para implementar una variedad de anotaciones y comprobaciones adicionales.

  2. los configuración predeterminada para comprobación de nulidad funciona bien: no nulo, excepto los locales (NNEL). Básicamente, esto significa que, de forma predeterminada, el verificador trata todo (variables de instancia, parámetros de método, tipos genéricos, etc.) excepto las variables locales como si tuvieran un tipo @NonNull por defecto. Por la documentación:

    El valor predeterminado de NNEL conduce al menor número de anotaciones explícitas en su código.

    Puede establecer un valor predeterminado diferente para una clase o para un método si NNEL no funciona para usted.

  3. Este marco te permite usar con sin crear una dependencia en el marco al adjuntar sus anotaciones en un comentario: p. /*@Nullable*/. Esto es bueno porque puede anotar y verificar una biblioteca o código compartido, pero aún así poder usar esa biblioteca / código compartido en otro proyecto que no usa el marco. Esta es una buena característica. Me he acostumbrado a usarlo, aunque tiendo a habilitar el marco de Checker en todos mis proyectos ahora.

  4. El marco tiene una manera de anotar API utiliza que no están anotados para la nulidad mediante el uso de archivos apéndice.


78
2018-02-10 22:59



Desde Oracle decidió no estandarizar @NonNull (y @Nullable) por el momento, 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 toolkit, 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.constraints o javax.annotation. El primero viene con JEE. Si esto es mejor que javax.annotation, que eventualmente podría llegar con JSE o no pasar nunca, es una cuestión de debate. Personalmente prefiero javax.annotation porque no me gustaría la dependencia de JEE.

Esto nos deja con

javax.annotation

que también es el más corto.

Solo hay una sintaxis que sería incluso mejor: java.annotation.Nullable. A medida que otros paquetes se graduaron de javax a java en el pasado, javax.annotation sea ​​un paso en la dirección correcta.

Implementación

Tenía la esperanza de que todos tengan básicamente la misma implementación trivial, pero un análisis detallado mostró que esto no es cierto.

Primero por las similitudes:

Las anotaciones @NonNull tienen todas la línea

public @interface NonNull {}

excepto por

  • org.jetbrains.annotations que lo llama @NotNull y tiene una implementación trivial
  • javax.annotation que tiene una implementación más larga
  • javax.validation.constraints que también lo llama @NotNull y tiene una implementación

Las anotaciones @Nullable tienen todas la línea

public @interface Nullable {}

a excepción de (nuevamente) org.jetbrains.annotations con su implementación trivial.

Por las diferencias:

Una llamativa es que

  • javax.annotation
  • javax.validation.constraints
  • org.checkerframework.checker.nullness.qual

todos tienen anotaciones de tiempo de ejecución (@Retention (RUNTIME), mientras

  • 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 más pequeño de lo que uno podría pensar, pero tienen el beneficio de herramientas de habilitación para hacer verificaciones de tiempo de ejecución además del compilar los del tiempo.

Otra diferencia importante es dónde en el código, las anotaciones se pueden usar. 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 LOCAL_VARIABLE
android.support.annotation X X X
edu.umd.cs.findbugs.annotations X X X X
org.jetbrains.annotation X X X X
lombok X X X X
javax.validation.constraints X X X

org.eclipse.jdt.annotation, javax.annotation y org.checkerframework.checker.nullness.qual usan 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 ayudarte a comparar más detalles tú mismo, enumero el código de cada anotación a continuación. Para facilitar la comparación, eliminé comentarios, importaciones y la anotación @Documented. (Todos tenían @Documented excepto por las clases del paquete de Android). Reordené los campos de líneas y @Target 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 implementaciones @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 {}

Los siguientes dos paquetes no tienen @Nullable, así que los incluyo por separado lombok tiene un @NonNull bastante aburrido. En javax.validation.constraints, @NonNull es en realidad un @NotNull y tiene una implementación larga.

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

Forma mi experiencia javax.annotation es al menos compatible con Eclipse y el marco de Checker fuera de la caja.

Resumen

Mi anotación ideal sería la sintaxis de java.annotation con la implementación de Checker Framework.

Si no tiene la intención de utilizar el Marco Checker, javax.annotation (JSR-305) sigue siendo su mejor apuesta por el momento.

Si está dispuesto a comprar en el marco de Checker simplemente use su org.checkerframework.checker.nullness.qual


Fuentes

  • android.support.annotation desde android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations de findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation de org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations from jetbrains-annotations-13.0.jar
  • javax.annotation de gwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qual de checker-framework-2.1.9.zip
  • lombok de lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints de validation-api-1.0.0.GA-sources.jar

60
2018-03-09 12:20



Yo uso el IntelliJ, porque estoy más preocupado con IntelliJ marcando cosas que pueden producir un NPE. Estoy de acuerdo en que es frustrante no tener una anotación estándar en el JDK. Se habla de agregarlo, podría convertirse en Java 7. ¡En cuyo caso habrá uno más para elegir!


49
2018-02-10 22:09



De acuerdo con la Lista de características de Java 7 Las anotaciones de tipo JSR-308 se difieren a Java 8. Las anotaciones JSR-305 ni siquiera se mencionan.

Hay un poco de información sobre el estado de JSR-305 en un apéndice del último borrador JSR-308. Esto incluye la observación de que las anotaciones JSR-305 parecen estar abandonadas. La página JSR-305 también lo muestra como "inactivo".

Mientras tanto, la respuesta pragmática es utilizar los tipos de anotación que son compatibles con las herramientas más utilizadas ... y prepárese para cambiarlas si la situación cambia.


De hecho, JSR-308 no define ningún tipo / clase de anotación, y parece que piensan que está fuera del alcance. (Y tienen razón, dada la existencia de JSR-305).

Sin embargo, si JSR-308 realmente parece convertirlo en Java 8, no me sorprendería si reviviera el interés en JSR-305. AFAIK, el equipo JSR-305 no ha abandonado formalmente su trabajo. Han estado callados por más de 2 años.

Es interesante que Bill Pugh (el líder tecnológico de JSR-305) es uno de los que están detrás de FindBugs.


31
2018-02-10 22:21



Para proyectos de Android debes usar android.support.annotation.NonNull y android.support.annotation.Nullable. Estas y otras anotaciones útiles específicas de Android están disponibles en Biblioteca de soporte.

De http://tools.android.com/tech-docs/support-annotations:

La biblioteca de soporte en sí también ha sido anotada con estos   anotaciones, de modo que como usuario de la biblioteca de soporte, Android Studio   ya verifique su código y señale los problemas potenciales basados ​​en estos   anotaciones


25
2017-09-05 09:51



JSR305 y FindBugs son escritos por la misma persona. Ambos están mal mantenidos, pero son lo más estándar posible y cuentan con el respaldo de todos los principales IDEs. La buena noticia es que funcionan bien tal como están.

Aquí se explica cómo aplicar @Nonnull a todas las clases, métodos y campos de forma predeterminada. Ver https://stackoverflow.com/a/13319541/14731 y https://stackoverflow.com/a/9256595/14731

  1. Definir @NotNullByDefault
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;


    /**
     * This annotation can be applied to a package, class or method to indicate that the class fields,
     * method return types and parameters in that element are not null by default unless there is: <ul>
     * <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
     * case the annotation of the corresponding parameter in the superclass applies) <li> there is a
     * default parameter annotation applied to a more tightly nested element. </ul>
     * <p/>
     * @see https://stackoverflow.com/a/9256595/14731
     */
    @Documented
    @Nonnull
    @TypeQualifierDefault(
    {
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.FIELD,
        ElementType.LOCAL_VARIABLE,
        ElementType.METHOD,
        ElementType.PACKAGE,
        ElementType.PARAMETER,
        ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NotNullByDefault
    {
    }

2. Agregue la anotación a cada paquete: package-info.java

@NotNullByDefault
package com.example.foo;

ACTUALIZAR: A partir del 12 de diciembre de 2012 JSR 305 aparece como "inactivo". De acuerdo con la documentación:

Una JSR que fue votada como "inactiva" por el Comité Ejecutivo, o una que ha llegado al final de su ciclo de vida natural.

Parece que JSR 308  es convirtiéndolo en JDK 8 y aunque el JSR no define @NotNull, el acompañante Checkers Framework hace. En el momento de escribir esto, el plugin Maven no se puede usar debido a este error: https://github.com/typetools/checker-framework/issues/183


16
2017-11-23 02:52



Si alguien solo está buscando las clases de IntelliJ: puede obtenerlas del repositorio de Maven con

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 

16
2018-04-25 14:15



Eclipse también tiene sus propias anotaciones.

org.eclipse.jdt.annotation.NonNull

Ver en http://wiki.eclipse.org/JDT_Core/Null_Analysis para detalles.


11
2017-09-17 11:10