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

999

Estoy buscando hacer que mi código sea más legible, así como utilizar herramientas como la inspección de código IDE y / o el análisis de código estático (FindBugs y Sonar) para evitar NullPointerExceptions. Muchas de las herramientas parecen incompatibles con la @NotNull/ @NonNull/ @Nonnullanotación de las demás y enumerarlas todas 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 la validación del tiempo de ejecución, no para el análisis estático.
    documentación

  • edu.umd.cs.findbugs.annotations.NonNull
    Utilizado por Findbugs análisis estático y, por tanto, Sonar (ahora sonarqube )
    la 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
    Utilizado por IntelliJ IDEA IDE para el análisis estático.
    documentación

  • lombok.NonNull
    Se usa para controlar la generación de código en el 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 la documentación del paquete de anotaciones de soporte

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

jaxzin
fuente
203
Apache debe inventar una anotación "común" y una herramienta que pueda convertirla en cualquier otra anotación. La solución al problema de demasiados estándares es inventar un nuevo estándar.
irreputable
66
@irreputable si apache inventa un nuevo "común", habría 56 versiones de él, superpuestas con otros proyectos. Y, de todos modos, no sería estándar (¡estándar! = Generalizado). Mejor usar algo realmente estándar, javax?. *. Por cierto, no hay "demasiados estándares" en esos ejemplos, solo veo 1 o 2.
ymajoros
66
javax.annotation.Nonnull funciona con findbugs (solo lo probé), lo cual es una razón convincente para que lo use.
Nicolas C
20
Si simplemente escribo @NotNull, se refiere a com.sun.istack.internal.NotNull. Dios mío ...
Thomas Weller
3
@MozartBrocchini: los opcionales son útiles en casos en los que podría haber utilizado previamente NullObjects. Sin embargo, en realidad no abordan el mismo objetivo que una anotación en tiempo de ejecución \ @NotNull, e introducen un tedioso desenvolvimiento.
Dave

Respuestas:

205

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
Ludwig Weinzierl
fuente
77
La desventaja javax.annotationes que es a) basado en un JSR muerto, b) difícil de encontrar un artefacto que solo proporcione las anotaciones y se mantenga. El de findbugs no es: search.maven.org/…
robinst el
18
Otro punto en contra javax.annotationes que causa problemas con Java 9 porque otros módulos también proporcionan clases en ese paquete (jax-ws).
robinst
10
@kevinarpe: El proyecto Findbugs está muerto y el proyecto sucesor Spotbugs está eliminando esas anotaciones: github.com/spotbugs/spotbugs/pull/180
robinst el
44
JSR 305 , que se habría estandarizado javax.annotation.NonNull, nunca se completó porque su plomo de especificación quedó AWOL. No tuvo nada que ver con ninguna decisión de Oracle.
Mark Reinhold
55
Otra razón para no usar jsr305.jar es que aparentemente viola la licencia binaria de Oracle Java: github.com/google/guava/issues/2960
Flujo
91

Me gusta mucho el Checker Framework , que es una implementación de anotaciones de tipo ( JSR-308 ) que se usa para implementar verificadores de defectos como un verificador de nulidad. Realmente no he intentado ningún otro para ofrecer una comparación, pero me ha encantado esta implementación.

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

Cuatro cosas que me gustan de este sistema:

  1. Tiene un verificador de defectos de nulidad (@Nullable), pero también tiene unos de inmutabilidad e internamiento (y otros). Uso el primero (nulidad) y estoy tratando de usar el segundo (inmutabilidad / IGJ). Estoy probando el tercero, pero todavía no estoy seguro de usarlo a largo plazo. Todavía no estoy convencido de la utilidad general de los otros verificadores, pero es bueno saber que el marco en sí mismo es un sistema para implementar una variedad de anotaciones y verificadores adicionales.

  2. La configuración predeterminada para la comprobación de nulidad funciona bien: no nulo excepto 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 de forma predeterminada. Por la documentación:

    El valor predeterminado de NNEL conduce a la menor cantidad de anotaciones explícitas en su código.

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

  3. Este marco le permite usarlo sin crear una dependencia en el marco al adjuntar sus anotaciones en un comentario: por ejemplo /*@Nullable*/. Esto es bueno porque puede anotar y verificar una biblioteca o código compartido, pero aún así puede 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 ahora tiendo a habilitar Checker Framework en todos mis proyectos.

  4. El marco tiene una forma de anotar las API que usa que aún no están anotadas por nulidad mediante el uso de archivos stub.

Bert F
fuente
3
Parece genial y me gustaría usarlo, pero no puedo. ¿Por qué GPL? ¿No podría ser la LGPL en su lugar?
Burkhard
13
De acuerdo con las preguntas frecuentes : "La licencia MIT más permisiva se aplica al código que quizás desee incluir en su propio programa, como las anotaciones".
seanf
1
Los enlaces están actualmente rotos. Pero +1 por los consejos sobre el uso de Checker Framework.
Paul Wagland
1
Es una pena que los verificadores de inmutabilidad se hayan eliminado en la última versión.
Franklin Yu
1
Checker Framework también se sugiere en los Tutoriales de Oracle Java .
Quazi Irfan
55

Yo uso el IntelliJ, porque me preocupa principalmente que IntelliJ marque cosas que podrían 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 llegar a Java 7. ¡En cuyo caso habrá uno más para elegir!

Sam Barnum
fuente
68
Actualización: IntelliJ ahora admite todas las anotaciones anteriores para resaltar el código, por lo que ya no está restringido a las anotaciones de IntelliJ: blogs.jetbrains.com/idea/2011/03/…
Daniel Alexiuc
31
¡Y también Eclipse Juno!
jFrenetic
55
javax.annotation.Nonnulles más ampliamente aceptado, ¿no es así?
Martin
1
@DanielAlexiuc Pero desafortunadamente, no los usa para sus controles de tiempo de ejecución, por lo que todavía hay un beneficio al usar los JetBrains ...
Trejkaz
44
@Trejkaz Desde 2016.3 crea controles de tiempo de ejecución para todos esos.
Karol S
32

De acuerdo con la lista de características de Java 7, las anotaciones de tipo JSR-308 se difieren a Java 8. Las anotaciones de 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 de 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 usar los tipos de anotación que son compatibles con las herramientas más utilizadas ... y estar preparado para cambiarlos 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 de alcance. (Y tienen razón, dada la existencia de JSR-305).

Sin embargo, si parece que JSR-308 realmente llega a Java 8, no me sorprendería si el interés en JSR-305 reviviera. 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) sea uno de los tipos detrás de FindBugs.

Stephen C
fuente
44
@pst: el calendario actual es para que Java 8 vaya a la versión general en septiembre de 2013 - infoq.com/news/2012/04/jdk-8-milestone-release-dates
Stephen C
2
Eso se ha deslizado hasta marzo de 2014 ahora: openjdk.java.net/projects/jdk8 . JSR 308 se incluye en la compilación M7 (consulte "104 - Anotaciones sobre tipos Java").
Stephen C
28

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

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

La biblioteca de soporte en sí también se ha anotado con estas anotaciones, por lo que, como usuario de la biblioteca de soporte, Android Studio ya verificará su código y señalará posibles problemas en función de estas anotaciones.

James Wald
fuente
3
Sería útil proporcionar una justificación para esa recomendación.
albaricoque
2
tools.android.com/tech-docs/support-annotations "La biblioteca de soporte en sí también se ha anotado con estas anotaciones, por lo que como usuario de la biblioteca de soporte, Android Studio ya verificará su código y marcará posibles problemas en función de estas anotaciones ".
James Wald
3
Por cierto, Android Studio admite jsr305 con javax.annotation.*anotaciones también
CAMOBAP
19

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> 
Bruno Eberhard
fuente
Este es el que hace que Intellij arroje advertencias, sí.
Haga clic en Upvote
La versión actual (a partir del 05/2017) es 15.0
BamaPookie
Tu derecho He actualizado la versión. Incluso si supongo que no ha cambiado mucho.
Bruno Eberhard
Tenga en cuenta que las anotaciones de JetBrains no se conservan durante el tiempo de ejecución, por lo que el soporte de Guice @Nullable, por ejemplo, no funciona con él.
Peter Major el
18

JSR305 y FindBugs son creados por la misma persona. Ambos están mal mantenidos, pero son tan estándar como es posible y cuentan con el respaldo de todos los IDE principales. La buena noticia es que funcionan bien 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;

ACTUALIZACIÓN : A partir del 12 de diciembre de 2012, JSR 305 figura como "inactivo". De acuerdo con la documentación:

Un JSR que fue votado como "inactivo" por el Comité Ejecutivo, o uno que ha llegado al final de su vida natural.

Parece que JSR 308 está llegando a JDK 8 y, aunque JSR no define a @NotNull, lo acompaña Checkers Framework. Al momento de escribir esto, el complemento Maven no se puede usar debido a este error: https://github.com/typetools/checker-framework/issues/183

Gili
fuente
2
El problema de showtopper para maven está solucionado. Entonces esto debería ser una opción nuevamente.
Marc von Renteln
Uso FindBugs a través de Maven, mi IDE no hace nada, esto evita las anotaciones específicas de IDE, ¿qué recomendarías?
Christophe Roussy
@ChristopheRoussy Su pregunta es específica del IDE. Por favor, abra una pregunta por separado.
Gili
15

Distinguir entre análisis estático y análisis de tiempo de ejecución. Use análisis estático para cosas internas y análisis de tiempo de ejecución para los límites públicos de su código.

Para cosas que no deberían ser nulas:

  • Verificación de tiempo de ejecución: use "if (x == null) ..." (dependencia cero) o @ javax.validation.NotNull (con validación de bean) o @ lombok.NonNull (simple y simple) o Prevasditions de guayabas. .)

    • Use Opcional para los tipos de devolución de métodos (solo). Java8 o guayaba.
  • Comprobación estática: use una anotación @NonNull

  • Donde quepa, use las anotaciones @ ... NonnullByDefault a nivel de clase o paquete. Cree estas anotaciones usted mismo (los ejemplos son fáciles de encontrar).
    • De lo contrario, use @ ... CheckForNull en el método devuelve para evitar NPE

Esto debería dar el mejor resultado: advertencias en el IDE, errores de Findbugs y checkerframework, excepciones significativas en tiempo de ejecución.

No espere que las comprobaciones estáticas sean maduras, su denominación no está estandarizada y diferentes bibliotecas e IDE los tratan de manera diferente, ignórelos. Las clases JSR305 javax.annotations. * Parecen estándar, pero no lo son, y causan paquetes divididos con Java9 +.

Algunas notas explicaciones:

  • Las anotaciones Findbugs / spotbugs / jsr305 con el paquete javax.validation. * Choque con otros módulos en Java9 +, también posiblemente violen la licencia de Oracle
  • Las anotaciones de Spotbugs aún dependen de las anotaciones de jsr305 / findbugs en tiempo de compilación (al momento de escribir https://github.com/spotbugs/spotbugs/issues/421 )
  • el nombre de jetbrains @NotNull entra en conflicto con @ javax.validation.NotNull.
  • las anotaciones de jetbrains, eclipse o checkersframework para la comprobación estática tienen la ventaja sobre las anotaciones javax de que no chocan con otros módulos en Java9 y versiones posteriores
  • @ javax.annotations.Nullable no significa Findbugs / Spotbugs lo que usted (o su IDE) cree que significa. Findbugs lo ignorará (en los miembros). Triste, pero cierto ( https://sourceforge.net/p/findbugs/bugs/1181 )
  • Para la comprobación estática fuera de un IDE, existen 2 herramientas gratuitas: Spotbugs (anteriormente Findbugs) y checkersframework.
  • La biblioteca Eclipse tiene @NonNullByDefault, jsr305 solo tiene @ParametersAreNonnullByDefault. Esos son simples envoltorios de conveniencia que aplican anotaciones base a todo en un paquete (o clase), puede crear fácilmente el suyo. Esto se puede usar en el paquete. Esto puede entrar en conflicto con el código generado (por ejemplo, lombok).
  • El uso de lombok como una dependencia exportada debe evitarse para las bibliotecas que comparte con otras personas, cuantas menos dependencias transitivas, mejor
  • El uso del marco de validación de Bean es poderoso, pero requiere una alta sobrecarga, por lo que es excesivo solo para evitar la verificación nula manual.
  • El uso de Opcional para campos y parámetros de métodos es controvertido (puede encontrar artículos al respecto fácilmente)
  • Las anotaciones nulas de Android son parte de la biblioteca de soporte de Android, vienen con muchas otras clases y no funcionan bien con otras anotaciones / herramientas

Antes de Java9, esta es mi recomendación:

// file: package-info.java
@javax.annotation.ParametersAreNonnullByDefault
package example;


// file: PublicApi
package example;

public interface PublicApi {

    Person createPerson(
        // NonNull by default due to package-info.java above
        String firstname,
        String lastname);
}

// file: PublicApiImpl
public class PublicApiImpl implements PublicApi {
    public Person createPerson(
            // In Impl, handle cases where library users still pass null
            @Nullable String firstname, // Users  might send null
            @Nullable String lastname // Users might send null
            ) {
        if (firstname == null) throw new IllagalArgumentException(...);
        if (lastname == null) throw new IllagalArgumentException(...);
        return doCreatePerson(fistname, lastname, nickname);
    }

    @NonNull // Spotbugs checks that method cannot return null
    private Person doCreatePerson(
             String firstname, // Spotbugs checks null cannot be passed, because package has ParametersAreNonnullByDefault
             String lastname,
             @Nullable String nickname // tell Spotbugs null is ok
             ) {
         return new Person(firstname, lastname, nickname);
    }

    @CheckForNull // Do not use @Nullable here, Spotbugs will ignore it, though IDEs respect it
    private Person getNickname(
         String firstname,
         String lastname) {
         return NICKNAMES.get(firstname + ':' + lastname);
    }
}

Tenga en cuenta que no hay forma de hacer que Spotbugs genere una advertencia cuando se desreferencia un parámetro de método anulable (en el momento de la escritura, versión 3.1 de Spotbugs). Quizás el checkerframework pueda hacer eso.

Lamentablemente, estas anotaciones no distinguen entre los casos de un método público de una biblioteca con sitios de llamadas arbitrarios y los métodos no públicos donde se puede conocer cada sitio de llamadas. Por lo tanto, el doble significado de: "Indica que nulo no es deseado, pero prepárate para que se pase nulo sin embargo" no es posible en una sola declaración, por lo tanto, el ejemplo anterior tiene diferentes anotaciones para la interfaz y la implementación.

Para los casos en que el enfoque de interfaz dividida no es práctico, el siguiente enfoque es un compromiso:

        public Person createPerson(
                @NonNull String firstname,
                @NonNull String lastname
                ) {
            // even though parameters annotated as NonNull, library clients might call with null.
            if (firstname == null) throw new IllagalArgumentException(...);
            if (lastname == null) throw new IllagalArgumentException(...);
            return doCreatePerson(fistname, lastname, nickname);
        }

Esto ayuda a los clientes a no pasar nulo (escribir el código correcto), al tiempo que devuelve errores útiles si lo hacen.

tkruse
fuente
Encontré esta respuesta solo ahora, pero @tkruse, ¿dónde encontraste esto: "Las anotaciones de Eclipse jdt no son aplicables a los retornos de métodos estáticos y algunos otros casos"? (la primera parte no es cierta, la segunda es bastante vaga :)).
Stephan Herrmann
@StephanHerrmann: No puedo recordar. Quité la viñeta.
tkruse
12

Eclipse también tiene sus propias anotaciones.

org.eclipse.jdt.annotation.NonNull

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

Horrocrux7
fuente
Parece que esto se integrará desde Eclipse 3.8 (Juno), lo que pondrá a Eclipse en línea con IntelliJ a este respecto. También debería permitirle configurar sus propias anotaciones nulas (por ejemplo, javax.annotation.Nonnull) y tiene una opción para que NotNull sea el predeterminado.
Motti Strom
11

Simplemente señalando que la API de Validación de Java ( javax.validation.constraints.*) no viene con una @Nullableanotación, lo cual es muy valioso en un contexto de análisis estático. Tiene sentido para la validación de beans de tiempo de ejecución, ya que este es el valor predeterminado para cualquier campo no primitivo en Java (es decir, nada que validar / aplicar). A los fines declarados que deberían pesar hacia las alternativas.

Ophir Radnitz
fuente
7

Desafortunadamente, JSR 308no agregaré más valores que la sugerencia local No nula de este proyecto aquí

Java 8no vendrá con una sola anotación predeterminada o su propio Checkermarco. Similar a Find-bugs o JSR 305, este JSR está mal mantenido por un pequeño grupo de equipos principalmente académicos.

No hay poder comercial detrás de esto, por lo tanto, se JSR 308lanza EDR 3(Early Draft Review en JCP) AHORA, mientras Java 8se supone que se enviará en menos de 6 meses: -O Similar a por 310cierto. pero a diferencia 308 Oraclede eso se ha hecho cargo de eso ahora lejos de sus fundadores para minimizar el daño que le hará a la Plataforma Java.

Cada proyecto, proveedor y clase académica como los que están detrás de Checker FrameworkyJSR 308 crearán su propia anotación de corrector patentada.

Haciendo código fuente incompatibles en los próximos años, hasta hace unos compromisos populares se podían encontrar y tal vez añaden a Java 9, o 10, o por medio de marcos como Apache Commonso Google Guava;-)

Werner Keil
fuente
7

Androide

Esta respuesta es específica de Android. Android tiene un paquete de soporte llamado support-annotations. Esto proporciona docenas de anotaciones específicas de Android y también proporciona comunes como NonNull, Nullableetc.

Para agregar el paquete de anotaciones de soporte , agregue la siguiente dependencia en su build.gradle:

compile 'com.android.support:support-annotations:23.1.1'

y luego usa:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}
Shubham Chaudhary
fuente
5

Mientras espera que esto se solucione en sentido ascendente (¿Java 8?), También podría definir su propio proyecto local @NotNully @Nullableanotaciones. Esto puede ser útil también en caso de que esté trabajando con Java SE, donde javax.validation.constraints no está disponible de forma predeterminada.

import java.lang.annotation.*;

/**
 * Designates that a field, return value, argument, or variable is
 * guaranteed to be non-null.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface NotNull {}

/**
 * Designates that a field, return value, argument, or variable may be null.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface Nullable {}

Sin duda, esto sería en gran medida con fines decorativos o a prueba de futuro, ya que lo anterior obviamente no agrega en sí mismo ningún soporte para el análisis estático de estas anotaciones.

Arto Bendiken
fuente
4

Si estás desarrollando para Android, eres algo vinculado a Eclipse (edición: al momento de escribir, ya no), que tiene sus propias anotaciones. Está incluido en Eclipse 3.8+ (Juno), pero está deshabilitado de forma predeterminada.

Puede habilitarlo en Preferencias> Java> Compilador> Errores / Advertencias> Análisis nulo (sección contraíble en la parte inferior).

Marque "Habilitar análisis nulo basado en anotaciones"

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage tiene recomendaciones sobre la configuración. Sin embargo, si tiene proyectos externos en su espacio de trabajo (como el SDK de Facebook), es posible que no cumplan con esas recomendaciones, y probablemente no desee solucionarlos con cada actualización del SDK ;-)

Yo suelo:

  1. Acceso de puntero nulo: error
  2. Infracción de la especificación nula: error (vinculado al punto n. ° 1)
  3. Posible acceso de puntero nulo: Advertencia (de lo contrario, el SDK de Facebook tendría advertencias)
  4. Conflicto entre anotaciones nulas e inferencia nula: Advertencia (vinculada al punto n. ° 3)
chaqke
fuente
44
atado a Eclipse? No es verdad.
dcow
1
@DavidCowden IntelliJ IDEA con soporte para desarrollo de Android, creo, estuvo disponible algún tiempo antes de que se introdujera AndroidStudio.
Mārtiņš Briedis
@ MārtiņšBriedis sí, eso es cierto. Creo que quisiste decir @chaqke.
dcow
Vale la pena señalar que Android e Intellij tienen anotaciones separadas, y probablemente seguirán siendo así hasta que Java incluya anotaciones oficiales. Estas son instrucciones para usar las anotaciones de eclipse con eclipse.
chaqke
Nunca ha estado vinculado a Eclipse. Puede usar cualquier IDE que desee.
DennisK
4

Si está trabajando en un proyecto grande, que puede ser mejor de crear su propio @Nullable y / o @NotNullanotaciones.

Por ejemplo:

@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)
@java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD,
                              java.lang.annotation.ElementType.METHOD,    
                              java.lang.annotation.ElementType.PARAMETER,
                              java.lang.annotation.ElementType.LOCAL_VARIABLE})
public @interface Nullable 
{
}

Si usa la política de retención correcta , las anotaciones no estarán disponibles en tiempo de ejecución . Desde ese punto de vista, es solo una cosa interna .

Aunque esto no es una ciencia estricta, creo que tiene más sentido usar una clase interna para ello.

  • Es una cosa interna. (sin impacto funcional o técnico)
  • Con muchos muchos muchos usos.
  • IDE como IntelliJ admite anotaciones @Nullable/ personalizadas @NotNull.
  • La mayoría de los marcos prefieren usar su propia versión interna también.

Preguntas adicionales (ver comentarios):

¿Cómo configurar esto en IntelliJ?

Haga clic en el "oficial de policía" en la esquina inferior derecha de la barra de estado de IntelliJ. Y haga clic en "Configurar inspecciones" en la ventana emergente. Próximo ... configurar anotaciones

bvdb
fuente
1
Intenté tu consejo, pero ideano cuentes nada sobrevoid test(@NonNull String s) {} llamado portest(null);
user1244932
3
@ user1244932 ¿Te refieres a IntelliJ IDEA? Puede configurar las anotaciones de nulabilidad que usa para el análisis estático. No sé exactamente dónde, pero un lugar para definirlos es en "Archivo> Configuración> Compilación, Ejecución, Implementación> Compilador" y hay un botón "Configurar anotaciones ...".
Adowrath
@ user1244932 vea la captura de pantalla si todavía está buscando esto.
bvdb
3

Ya hay demasiadas respuestas aquí, pero (a) es 2019, y todavía no hay un "estándar" Nullabley (b) ninguna otra respuesta hace referencia a Kotlin.

La referencia a Kotlin es importante, porque Kotlin es 100% interoperable con Java y tiene una característica central de seguridad nula. Al llamar a las bibliotecas de Java, puede aprovechar esas anotaciones para informar a las herramientas de Kotlin si una API de Java puede aceptar o devolver null.

Hasta donde yo sé, los únicos Nullablepaquetes compatibles con Kotlin son org.jetbrains.annotationsy android.support.annotation(ahoraandroidx.annotation ). Este último solo es compatible con Android, por lo que no se puede usar en proyectos JVM / Java / Kotlin que no sean Android. Sin embargo, el paquete JetBrains funciona en todas partes.

Entonces, si desarrolla paquetes Java que también deberían funcionar en Android y Kotlin (y que sean compatibles con Android Studio e IntelliJ), su mejor opción es probablemente el paquete JetBrains.

Maven

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

Gradle:

implementation 'org.jetbrains:annotations-java5:15.0'
noamtm
fuente
2
Hmm, esto dice lo contrario: kotlinlang.org/docs/reference/…
skagedal
3

Hay otra forma de hacer esto en Java 8. Estoy haciendo 2 cosas para lograr lo que necesitaba:

  1. Hacer explícitos los campos anulables con tipos envolviendo campos anulables con java.util.Optional
  2. Comprobando que todos los campos no anulables no son nulos en tiempo de construcción con java.util.Objects.requireNonNull

Ejemplo:

import static java.util.Objects.requireNonNull;

public class Role {

  private final UUID guid;
  private final String domain;
  private final String name;
  private final Optional<String> description;

  public Role(UUID guid, String domain, String name, Optional<String> description) {
    this.guid = requireNonNull(guid);
    this.domain = requireNonNull(domain);
    this.name = requireNonNull(name);
    this.description = requireNonNull(description);
  }

Entonces mi pregunta es, ¿necesitamos incluso anotar cuando usamos Java 8?

Editar: descubrí más tarde que algunos consideran una mala práctica para usar Optionalen argumentos, hay una buena discusión con pros y contras aquí ¿Por qué no debería usarse Java 8's Opcional en argumentos?

Opción alternativa dado que no se recomienda usar Opcional en los argumentos, necesitamos 2 constructores:

  //Non null description
  public Role(UUID guid, String domain, String name, String description) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);

        // description will never be null
        requireNonNull(description);

        // but wrapped with an Optional
        this.description = Optional.of(description);
      }

  // Null description is assigned to Optional.empty
  public Role(UUID guid, String domain, String name) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);
        this.description = Optional.empty();
      }
Mozart Brocchini
fuente
Diría que todavía necesita la anotación @NotNull para los 4 parámetros formales para que los verificadores de análisis estático conozcan su intención de que ninguno sea nulo. Todavía no hay nada en el lenguaje Java que lo imponga. También debe verificar que la descripción no sea nula si está programando a la defensiva.
jaxzin
2
Todavía puedo escribir este código: new Role(null,null,null,null);. Con las anotaciones, mi IDE y el análisis estático advertirán que no se puede pasar nulo a esos parámetros. Sin él, no lo descubro hasta que ejecuto el código. Ese es el valor de las anotaciones.
jaxzin
2
También estoy en entornos donde los desarrolladores pueden usar cualquier IDE o editor de texto que prefieran, no es mutuamente exclusivo. Luego también integramos el plugin maven-pmd-y / o SonarQube en el proceso de construcción para alentar y resaltar, e incluso bloquear, problemas de calidad de código antes de la fusión, por ejemplo, en solicitudes de extracción.
jaxzin 01 de
2
Opcional no está destinado a ser utilizado como argumento de método o campo privado. Ver por ejemplo: stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
assylias
1
@assylias sí, descubrí eso más tarde, dicen que no es recomendable porque no nos comprará nada, definitivamente puedo entender su racionalidad. En este caso que puse aquí, uno podría hacer que el argumento description no sea nulo y el código del cliente podría pasar una cadena vacía, pero en muchos casos podría ser útil distinguir entre una cadena vacía y no tener un valor. Gracias por tu comentario. Actualizaré la respuesta.
Mozart Brocchini
2

¿El sol no tiene el suyo ahora? Qué es esto:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

Esto parece estar empaquetado con todas las versiones de Java que he usado en los últimos años.

Editar: como se menciona en los comentarios a continuación, probablemente no quiera usarlos. En ese caso, ¡mi voto es por las anotaciones de IntelliJ jetbrains!

Nate W.
fuente
10
No tengo idea de qué es, pero el nombre del paquete debe ser una GRAN PISTA de que NO está destinado para uso general.
Stephen C
3
Por lo general, se abstiene de usar clases en el espacio de nombres com.sun ya que son internas; no destinado para uso directo; y sin garantías en cuanto a su disponibilidad o comportamiento futuro. Uno debe tener un caso realmente sólido para usar directamente un artefacto com.sun.
luis.espinal
más algo está representada en tal formato HTML pobres (en Java2s.com para colmo) le proporcionará algunas banderas rojas :)
luis.espinal
2

Una de las cosas buenas de IntelliJ es que no necesita usar sus anotaciones. Puede escribir la suya, o puede usar las de cualquier otra herramienta que desee. Ni siquiera estás limitado a un solo tipo. Si está utilizando dos bibliotecas que usan anotaciones @NotNull diferentes, puede decirle a IntelliJ que use ambas. Para hacer esto, vaya a "Configurar inspecciones", haga clic en la inspección "Condiciones constantes y excepciones" y presione el botón "Configurar inspecciones". Uso el Comprobador de nulidad siempre que puedo, así que configuré IntelliJ para usar esas anotaciones, pero puedes hacer que funcione con cualquier otra herramienta que desees. (No tengo opinión sobre las otras herramientas porque he estado usando las inspecciones de IntelliJ durante años, y me encantan).

Miguel Muñoz
fuente
1

Otra opción son las anotaciones proporcionadas con ANTLR 4. Luego de la solicitud de extracción # 434 , el artefacto que contiene las anotaciones @NotNully @Nullableincluye un procesador de anotaciones que produce errores y / o advertencias en tiempo de compilación en caso de que uno de estos atributos se use incorrectamente (por ejemplo, si ambos se aplican al mismo elemento, o si @Nullablese aplica a un elemento con un tipo primitivo). El procesador de anotaciones proporciona una garantía adicional durante el proceso de desarrollo de software de que la información transmitida por la aplicación de estas anotaciones es precisa, incluso en casos de herencia de métodos.

Sam Harwell
fuente
1

Si está compilando su aplicación utilizando Spring Framework, le sugiero que utilice javax.validation.constraints.NotNullComming Beans Validation empaquetado en la siguiente dependencia:

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>

La principal ventaja de esta anotación es que Spring proporciona soporte tanto para los parámetros del método como para los campos de clase con anotaciones javax.validation.constraints.NotNull. Todo lo que necesita hacer para habilitar el soporte es:

  1. suministre el jar api para la validación de beans y jar con la implementación del validador de anotaciones jsr-303 / jsr-349 (que viene con la dependencia de Hibernate Validator 5.x):

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.4.1.Final</version>
    </dependency>
  2. proporcionar MethodValidationPostProcessor al contexto de spring

      @Configuration
      @ValidationConfig
      public class ValidationConfig implements MyService {
    
            @Bean
            public MethodValidationPostProcessor providePostProcessor() {
                  return new MethodValidationPostProcessor()
            }
      }
  3. finalmente anota tus clases con Spring org.springframework.validation.annotation.Validatedy Spring validará la validación automáticamente.

Ejemplo:

@Service
@Validated
public class MyServiceImpl implements MyService {

  @Override
  public Something doSomething(@NotNull String myParameter) {
        // No need to do something like assert myParameter != null  
  }
}

Cuando intente llamar al método doSomething y pasar nulo como valor del parámetro, se lanzará spring (por medio de HibernateValidator) ConstraintViolationException. No es necesario trabajar manualmente aquí.

También puede validar los valores de retorno.

Otro beneficio importante de javax.validation.constraints.NotNullvenir para Beans Validation Framework es que, por el momento, todavía está desarrollado y se planean nuevas características para la nueva versión 2.0.

¿Qué hay de @Nullable? No hay nada como eso en Beans Validation 1.1. Bueno, podría argumentar que si decides usarlo, @NotNulltodo lo que NO esté anotado @NonNulles efectivamente "anulable", entonces la @Nullableanotación es inútil.

walkeros
fuente
1
Por favor no lo use. Se utiliza para la validación del tiempo de ejecución, NO para el análisis de código estático. Ver justsomejavaguy.blogspot.com/2011/08/… para más detalles. Fuente: respuesta BORRADA con 219 votos por @ luis.espinal.
koppor
@koppor: No estoy de acuerdo. Si esto no está destinado al uso, ¿por qué Spring lo manejaría en tiempo de ejecución? Además, el marco de validación de Beans permite crear anotaciones únicamente para el análisis de tiempo de ejecución, ya que permite acceder al objeto Context (actualmente anotado / validado instancje) en tiempo de ejecución.
walkeros