Quelle annotation Java @NotNull dois-je utiliser?


1000

Je cherche à rendre mon code plus lisible et à utiliser des outils comme l'inspection de code IDE et / ou l'analyse de code statique (FindBugs et Sonar) pour éviter les exceptions NullPointerExceptions. De nombreux outils semblent incompatibles avec l' annotation @NotNull/ @NonNull/ de l'autre @Nonnullet les énumérer tous dans mon code serait terrible à lire. Avez-vous des suggestions dont la meilleure? Voici la liste des annotations équivalentes que j'ai trouvées:

  • javax.validation.constraints.NotNull
    Créé pour la validation d'exécution, pas pour l'analyse statique.
    Documentation

  • edu.umd.cs.findbugs.annotations.NonNull
    Utilisé par Findbugs analyse statique et donc Sonar (maintenant sonarqube )
    documentation

  • javax.annotation.Nonnull
    Cela peut également fonctionner avec Findbugs, mais JSR-305 est inactif. (Voir aussi: Quel est le statut de JSR 305? ) Source

  • org.jetbrains.annotations.NotNull
    Utilisé par IntelliJ IDEA IDE pour l'analyse statique.
    Documentation

  • lombok.NonNull
    Utilisé pour contrôler la génération de code dans Project Lombok .
    Annotation d'espace réservé car il n'y a pas de norme.
    source , documentation

  • android.support.annotation.NonNull
    Annotation de marqueur disponible dans Android, fournie par la documentation du package support-annotations

  • org.eclipse.jdt.annotation.NonNull
    Utilisé par Eclipse pour la documentation d' analyse de code statique


203
apache devrait inventer une annotation "commune" et un outil qui peut la convertir en n'importe quelle autre annotation. la solution au problème de trop de normes est d'inventer une nouvelle norme.
irréputable le

6
@irreputable si apache invente un nouveau "commun", il y en aurait 56 versions, se chevauchant avec d'autres projets. Et, ce ne serait pas standard de toute façon (standard! = Répandu). Mieux vaut utiliser quelque chose de vraiment standard, javax?. *. BTW, il n'y a pas "trop ​​de normes" dans ces exemples, je vois juste 1 ou 2.
ymajoros

6
javax.annotation.Nonnull fonctionne avec findbugs (je viens de le tester), ce qui est une raison convaincante pour moi de l'utiliser.
Nicolas C

20
Si j'écris simplement @NotNull, cela fait référence à com.sun.istack.internal.NotNull. OMG ...
Thomas Weller

3
@MozartBrocchini - Les options sont utiles dans les cas où vous avez peut-être déjà utilisé NullObjects. Ils n'abordent pas vraiment le même objectif qu'une annotation d'exécution \ @NotNull, et ils introduisent un déballage fastidieux.
Dave

Réponses:


205

Depuis JSR 305 (dont l'objectif était de normaliser @NonNullet @Nullable) est en sommeil depuis plusieurs années, je crains qu'il n'y ait pas de bonne réponse. Tout ce que nous pouvons faire est de trouver une solution pragmatique et la mienne est la suivante:

Syntaxe

D'un point de vue purement stylistique, je voudrais éviter toute référence à l'IDE, au framework ou à toute boîte à outils à l'exception de Java lui-même.

Cela exclut:

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations
  • org.checkerframework.checker.nullness.qual
  • lombok.NonNull

Ce qui nous laisse avec javax.validation.constraintsou javax.annotation. Le premier est livré avec JEE. Si c'est mieux que cela javax.annotation, qui pourrait éventuellement arriver avec JSE ou jamais du tout, c'est un sujet de débat. Personnellement, je préfère javax.annotationparce que je n'aimerais pas la dépendance JEE.

Cela nous laisse avec

javax.annotation

qui est aussi le plus court.

Il n'y a qu'une syntaxe qui serait même mieux: java.annotation.Nullable. Comme d'autres packages sont passés de javaxà javadans le passé, l'annotation javax serait un pas dans la bonne direction.

la mise en oeuvre

J'espérais qu'ils ont tous fondamentalement la même implémentation triviale, mais une analyse détaillée a montré que ce n'est pas vrai.

D'abord pour les similitudes:

Les @NonNullannotations ont toutes la ligne

public @interface NonNull {}

à l'exception de

  • org.jetbrains.annotationsqui l'appelle @NotNullet a une implémentation triviale
  • javax.annotation qui a une mise en œuvre plus longue
  • javax.validation.constraintsqui l'appelle aussi @NotNullet a une implémentation

Les @Nullableannotations ont toutes la ligne

public @interface Nullable {}

sauf (encore) le org.jetbrains.annotationsavec leur implémentation triviale.

Pour les différences:

Un frappant est que

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

tous ont des annotations d'exécution ( @Retention(RUNTIME)), tandis que

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations

ne sont que du temps de compilation ( @Retention(CLASS)).

Comme décrit dans cette réponse SO, l'impact des annotations d'exécution est plus petit qu'on ne le pense, mais elles ont l'avantage d'activer des outils pour effectuer des vérifications d'exécution en plus de celles de la compilation.

Une autre différence importante est l' endroit où dans le code les annotations peuvent être utilisées. Il existe deux approches différentes. Certains packages utilisent des contextes de style JLS 9.6.4.1. Le tableau suivant donne un aperçu:

                                PARAMÈTRE DE MÉTHODE DE CHAMP 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.annotationEt org.checkerframework.checker.nullness.qualutiliser les contextes définis dans JLS 4,11, ce qui est à mon avis la bonne façon de le faire.

Cela nous laisse avec

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

dans ce tour.

Code

Pour vous aider à comparer vous-même d'autres détails, j'énumère ci-dessous le code de chaque annotation. Pour faciliter la comparaison, j'ai supprimé les commentaires, les importations et l' @Documentedannotation. (ils avaient tous @Documentedsauf les classes du package Android). J'ai réorganisé les lignes et les @Targetchamps et normalisé les qualifications.

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 {}

Pour être complet, voici les @Nullableimplémentations:

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 {}

Les deux packages suivants n'en ont pas @Nullable, je les énumère donc séparément; Lombok a un assez ennuyeux @NonNull. Dans javax.validation.constraintsle @NonNullest en fait un @NotNull et il a une implémentation assez longue.

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();
    }
}

Soutien

D'après mon expérience, javax.annotationest au moins pris en charge par Eclipse et le Checker Framework hors de la boîte.

Sommaire

Mon annotation idéale serait la java.annotationsyntaxe avec l'implémentation de Checker Framework.

Si vous n'avez pas l'intention d'utiliser le Checker Framework, le javax.annotation( JSR-305 ) reste votre meilleur pari pour le moment.

Si vous êtes prêt à acheter dans le Checker Framework, utilisez simplement leur org.checkerframework.checker.nullness.qual.


Sources

  • android.support.annotation de 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 de 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
  • lombokde lombokcommitf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints de validation-api-1.0.0.GA-sources.jar

7
L'inconvénient javax.annotationest qu'il est a) basé sur un JSR mort, b) difficile de trouver un artefact qui fournit simplement les annotations et est maintenu. Celui de findbugs n'est pas: search.maven.org/…
robinst

18
Un autre point contre javax.annotationest qu'il cause des problèmes avec Java 9 car d'autres modules fournissent également des classes dans ce package (jax-ws).
robinst

10
@kevinarpe: Le projet Findbugs est mort et le projet successeur Spotbugs supprime ces annotations: github.com/spotbugs/spotbugs/pull/180
robinst

4
Le JSR 305 , qui aurait été standardisé javax.annotation.NonNull, n'a jamais été achevé car son avance en matière de spécifications a disparu. Cela n'avait rien à voir avec une décision d'Oracle.
Mark Reinhold

5
Une autre raison de ne pas utiliser jsr305.jar est qu'il viole apparemment la licence binaire Oracle Java: github.com/google/guava/issues/2960
Flow du

91

J'aime beaucoup le Checker Framework , qui est une implémentation d'annotations de type ( JSR-308 ) qui est utilisée pour implémenter des vérificateurs de défauts comme un vérificateur de nullité. Je n'ai pas vraiment essayé d'autres pour proposer une comparaison, mais j'ai été satisfait de cette implémentation.

Je ne suis pas affilié au groupe qui propose le logiciel, mais je suis fan.

Quatre choses que j'aime dans ce système:

  1. Il a un vérificateur de défauts pour la nullité (@Nullable), mais a également ceux pour l' immuabilité et l' internement (et autres). J'utilise le premier (nullité) et j'essaie de me lancer dans le second (immuabilité / IGJ). J'essaie le troisième, mais je ne suis pas encore sûr de l'utiliser à long terme. Je ne suis pas encore convaincu de l'utilité générale des autres vérificateurs, mais il est bon de savoir que le cadre lui-même est un système pour implémenter une variété d'annotations et de vérificateurs supplémentaires.

  2. Le paramètre par défaut pour la vérification de la nullité fonctionne bien: Non nul sauf les locaux (NNEL). Fondamentalement, cela signifie que par défaut, le vérificateur traite tout (variables d'instance, paramètres de méthode, types génériques, etc.) à l'exception des variables locales comme si elles avaient un type @NonNull par défaut. Selon la documentation:

    La valeur par défaut NNEL conduit au plus petit nombre d'annotations explicites dans votre code.

    Vous pouvez définir une valeur par défaut différente pour une classe ou pour une méthode si NNEL ne fonctionne pas pour vous.

  3. Ce framework vous permet d'utiliser avec sans créer de dépendance sur le framework en enfermant vos annotations dans un commentaire: eg /*@Nullable*/. C'est bien parce que vous pouvez annoter et vérifier une bibliothèque ou un code partagé, mais toujours pouvoir utiliser cette bibliothèque / codée partagée dans un autre projet qui n'utilise pas le framework. C'est une fonctionnalité intéressante. Je me suis habitué à l'utiliser, même si j'ai tendance à activer le Checker Framework sur tous mes projets maintenant.

  4. Le cadre a un moyen d' annoter les API que vous utilisez qui ne sont pas déjà annotées pour la nullité en utilisant des fichiers de raccord.


3
Cela semble génial et j'aimerais l'utiliser, mais je ne peux pas. Pourquoi GPL? Ne serait-ce pas la LGPL à la place?
Burkhard

13
Selon la FAQ : "La licence MIT plus permissive s'applique au code que vous voudrez peut-être inclure dans votre propre programme, comme les annotations."
seanf

1
Les liens sont actuellement rompus. Mais +1 pour les conseils sur l'utilisation de Checker Framework.
Paul Wagland

1
Il est dommage que les vérificateurs d'immuabilité soient supprimés dans la dernière version.
Franklin Yu

1
Checker Framework est également suggéré dans Oracle Java Tutorials .
Quazi Irfan

55

J'utilise celui d'IntelliJ, car je suis principalement préoccupé par le signalement d'IntelliJ qui pourrait produire un NPE. Je suis d'accord que c'est frustrant de ne pas avoir d'annotation standard dans le JDK. Il est question de l'ajouter, il pourrait en faire Java 7. Dans ce cas, il y en aura un de plus à choisir!


68
Mise à jour: IntelliJ prend désormais en charge toutes les annotations ci-dessus pour la mise en évidence du code, vous n'êtes donc plus limité aux annotations d'IntelliJ: blogs.jetbrains.com/idea/2011/03/…
Daniel Alexiuc

31
Et Eclipse Juno aussi!
jFrenetic

5
javax.annotation.Nonnullest plus largement accepté, n'est-ce pas?
Martin

1
@DanielAlexiuc Mais malheureusement, il ne les utilise pas pour ses vérifications d'exécution, il y a donc toujours un avantage à utiliser ceux JetBrains ...
Trejkaz

4
@Trejkaz Depuis 2016.3, il crée des contrôles d'exécution pour tous ces éléments.
Karol S

32

Selon la liste des fonctionnalités de Java 7, les annotations de type JSR-308 sont reportées à Java 8. Les annotations JSR-305 ne sont même pas mentionnées.

Il y a un peu d'informations sur l'état de JSR-305 dans une annexe du dernier projet de JSR-308. Cela inclut l'observation que les annotations JSR-305 semblent être abandonnées. La page JSR-305 le montre également comme "inactif".

En attendant, la réponse pragmatique est d'utiliser les types d'annotation pris en charge par les outils les plus utilisés ... et d'être prêt à les changer si la situation change.


En fait, JSR-308 ne définit aucun type / classe d'annotation, et il semble qu'ils pensent qu'il est hors de portée. (Et ils ont raison, étant donné l'existence de JSR-305).

Cependant, si JSR-308 a vraiment l'air de devenir Java 8, cela ne me surprendrait pas si l'intérêt pour JSR-305 revenait. AFAIK, l'équipe JSR-305 n'a pas officiellement abandonné son travail. Ils sont juste silencieux depuis plus de 2 ans.

Il est intéressant de noter que Bill Pugh (le responsable technique de JSR-305) est l'un des gars derrière FindBugs.


4
@pst - le calendrier actuel est pour que Java 8 passe en version générale en septembre 2013 - infoq.com/news/2012/04/jdk-8-milestone-release-dates
Stephen C

2
Cela a glissé à mars 2014 maintenant - openjdk.java.net/projects/jdk8 . JSR 308 est inclus dans la build M7 (regardez dans "104 - Annotations sur les types Java").
Stephen C

28

Pour les projets Android, vous devez utiliser android.support.annotation.NonNullet android.support.annotation.Nullable. Ces annotations utiles et spécifiques à Android sont disponibles dans la bibliothèque de support .

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

La bibliothèque de support elle-même a également été annotée avec ces annotations, donc en tant qu'utilisateur de la bibliothèque de support, Android Studio vérifiera déjà votre code et signalera les problèmes potentiels en fonction de ces annotations.


3
Il serait utile de justifier cette recommandation.
abricot

2
tools.android.com/tech-docs/support-annotations "La bibliothèque de support elle-même a également été annotée avec ces annotations. En tant qu'utilisateur de la bibliothèque de support, Android Studio vérifiera déjà votre code et signalera les problèmes potentiels en fonction de ces annotations . "
James Wald du

3
BTW Android Studio prend en charge jsr305 avec des javax.annotation.*annotations également
CAMOBAP

19

Si quelqu'un cherche simplement les classes IntelliJ: vous pouvez les obtenir à partir du référentiel maven avec

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

C'est celui qui fait que Intellij émet des avertissements, oui.
Cliquez sur Upvote

La version actuelle (au 05/2017) est 15.0
BamaPookie

Tu as raison. J'ai mis à jour la version. Même si je suppose que cela n'a pas beaucoup changé.
Bruno Eberhard

Gardez à l'esprit que les annotations JetBrains ne sont pas conservées pour l'exécution, donc la prise en charge de Guice @Nullable par exemple ne fonctionne pas avec.
Peter Major

18

JSR305 et FindBugs sont créés par la même personne. Les deux sont mal entretenus mais sont aussi standard que possible et sont pris en charge par tous les principaux IDE. La bonne nouvelle, c'est qu'ils fonctionnent bien tels quels.

Voici comment appliquer @Nonnull à toutes les classes, méthodes et champs par défaut. Voir https://stackoverflow.com/a/13319541/14731 et https://stackoverflow.com/a/9256595/14731

  1. Définir @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. Ajoutez l'annotation à chaque package: package-info.java

@NotNullByDefault
package com.example.foo;

MISE À JOUR : Au 12 décembre 2012, le JSR 305 est répertorié comme "Dormant". Selon la documentation:

Un JSR qui a été voté "dormant" par le Comité Exécutif, ou qui a atteint la fin de sa durée de vie naturelle.

Il semble que JSR 308 soit en train de devenir JDK 8 et bien que le JSR ne définisse pas @NotNull, l'accompagnant le Checkers Frameworkfait. Au moment d'écrire ces lignes, le plugin Maven est inutilisable en raison de ce bogue: https://github.com/typetools/checker-framework/issues/183


2
Le problème de showstopper pour maven est résolu. Cela devrait donc être une option à nouveau.
Marc von Renteln

J'utilise FindBugs via Maven, rien n'est fait par mon IDE, cela évite les annotations spécifiques à l'IDE, que recommanderiez-vous?
Christophe Roussy

@ChristopheRoussy Votre question est spécifique à l'IDE. Veuillez ouvrir une question distincte.
Gili

15

Distinguer l'analyse statique de l'analyse d'exécution. Utilisez l'analyse statique pour les éléments internes et l'analyse d'exécution pour les limites publiques de votre code.

Pour les choses qui ne doivent pas être nulles:

  • Vérification de l'exécution: utilisez "if (x == null) ..." (dépendance zéro) ou @ javax.validation.NotNull (avec validation du bean) ou @ lombok.NonNull (clair et simple) ou guavas Preconditions.checkNotNull (.. .)

    • Utilisez Facultatif pour les types de retour de méthode (uniquement). Soit Java8 ou Guava.
  • Vérification statique: utilisez une annotation @NonNull

  • Lorsque cela convient, utilisez les annotations @ ... NonnullByDefault au niveau de la classe ou du package. Créez vous-même ces annotations (les exemples sont faciles à trouver).
    • Sinon, utilisez @ ... CheckForNull sur les retours de méthode pour éviter les NPE

Cela devrait donner le meilleur résultat: avertissements dans l'EDI, erreurs par Findbugs et checkerframework, exceptions d'exécution significatives.

Ne vous attendez pas à ce que les vérifications statiques soient matures, leur dénomination n'est pas normalisée et les différentes bibliothèques et IDE les traitent différemment, ignorez-les. Les classes JSR305 javax.annotations. * Ressemblent à la norme, mais elles ne le sont pas, et elles provoquent des packages fractionnés avec Java9 +.

Quelques explications des notes:

  • Les annotations Findbugs / spotbugs / jsr305 avec le package javax.validation. * Se heurtent à d'autres modules en Java9 +, et peuvent également violer la licence Oracle
  • Les annotations Spotbugs dépendent toujours des annotations jsr305 / findbugs lors de la compilation (au moment de la rédaction de https://github.com/spotbugs/spotbugs/issues/421 )
  • jetbrains @NotNull name est en conflit avec @ javax.validation.NotNull.
  • les annotations jetbrains, eclipse ou checkersframework pour la vérification statique ont l'avantage sur javax.annotations de ne pas entrer en conflit avec d'autres modules en Java9 et supérieur
  • @ javax.annotations.Nullable ne signifie pas pour Findbugs / Spotbugs ce que vous (ou votre IDE) pensez que cela signifie. Findbugs l'ignorera (sur les membres). Triste, mais vrai ( https://sourceforge.net/p/findbugs/bugs/1181 )
  • Pour la vérification statique en dehors d'un IDE, 2 outils gratuits existent: Spotbugs (anciennement Findbugs) et checkersframework.
  • La bibliothèque Eclipse a @NonNullByDefault, jsr305 n'a que @ParametersAreNonnullByDefault. Ce sont de simples wrappers de commodité appliquant des annotations de base à tout dans un package (ou une classe), vous pouvez facilement créer le vôtre. Cela peut être utilisé sur l'emballage. Cela peut entrer en conflit avec le code généré (par exemple lombok).
  • L'utilisation de lombok en tant que dépendance exportée doit être évitée pour les bibliothèques que vous partagez avec d'autres personnes, moins les dépendances transitives sont bonnes, mieux c'est
  • L'utilisation du framework de validation Bean est puissante, mais nécessite des frais généraux élevés, donc c'est exagéré juste pour éviter la vérification manuelle des valeurs nulles.
  • L'utilisation des paramètres facultatifs pour les champs et les méthodes est controversée (vous pouvez facilement trouver des articles à ce sujet)
  • Les annotations nulles Android font partie de la bibliothèque de support Android, elles sont livrées avec beaucoup d'autres classes et ne jouent pas bien avec d'autres annotations / outils

Avant Java9, voici ma recommandation:

// 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);
    }
}

Notez qu'il n'y a aucun moyen de faire en sorte que Spotbugs émette un avertissement lorsqu'un paramètre de méthode nullable est déréférencé (au moment de la rédaction, la version 3.1 de Spotbugs). Peut-être que checkerframework peut le faire.

Malheureusement, ces annotations ne font pas de distinction entre les cas d'une méthode publique d'une bibliothèque avec des appels arbitraires et des méthodes non publiques où chaque site d'appel peut être connu. Ainsi, la double signification de: "Indiquez que null est indésirable, mais préparez-vous à ce que null soit passé néanmoins" n'est pas possible dans une seule déclaration, par conséquent l'exemple ci-dessus a des annotations différentes pour l'interface et l'implémentation.

Dans les cas où l'approche d'interface fractionnée n'est pas pratique, l'approche suivante est un compromis:

        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);
        }

Cela aide les clients à ne pas passer null (écriture du code correct), tout en renvoyant des erreurs utiles s'ils le font.


J'ai trouvé cette réponse seulement maintenant, mais @tkruse, où avez-vous trouvé ceci: "Les annotations Eclipse jdt ne sont pas applicables aux retours de méthodes statiques et dans certains autres cas"? (la première partie n'est pas vraie, la seconde assez vague :)).
Stephan Herrmann

@StephanHerrmann: Je ne me souviens pas. J'ai supprimé la puce.
tkruse

12

Eclipse a également ses propres annotations.

org.eclipse.jdt.annotation.NonNull

Voir sur http://wiki.eclipse.org/JDT_Core/Null_Analysis pour plus de détails.


Il semble que cela va être intégré à partir d'Eclipse 3.8 (Juno), ce qui mettra Eclipse en ligne avec IntelliJ à cet égard. Il devrait également vous permettre de configurer vos propres annotations Null (par exemple javax.annotation.Nonnull) et a une option pour avoir NotNull par défaut.
Motti Strom

11

Je souligne simplement que l'API Java Validation ( javax.validation.constraints.*) ne comporte pas d' @Nullableannotation, ce qui est très utile dans un contexte d'analyse statique. Cela a du sens pour la validation du bean d'exécution car c'est la valeur par défaut pour tout champ non primitif en Java (c'est-à-dire rien à valider / appliquer). Aux fins énoncées, cela devrait peser vers les alternatives.


7

Malheureusement, JSR 308n'ajoutera pas plus de valeurs que cette suggestion locale de projet non nul ici

Java 8ne viendra pas avec une seule annotation par défaut ou son propre Checkerframework. Semblable à Find-bugs ou JSR 305, ce JSR est mal entretenu par un petit groupe d'équipes principalement académiques.

Aucune puissance commerciale derrière elle, JSR 308lance donc EDR 3(Early Draft Review at JCP) MAINTENANT, alors qu'il Java 8est censé être expédié en moins de 6 mois: -O Similaire à 310btw. mais contrairement à 308 Oraclea pris en charge cela maintenant loin de ses fondateurs pour minimiser les dommages qu'il fera à la plate-forme Java.

Chaque projet, fournisseur et classe académique comme ceux derrière le Checker FrameworketJSR 308 créera sa propre annotation propriétaire de vérificateur.

Rendre le code source incompatible pour les années à venir, jusqu'à ce que quelques compromis populaires puissent être trouvés et peut-être ajoutés à Java 9ou 10, ou via des cadres comme Apache Commonsou Google Guava;-)


7

Android

Cette réponse est spécifique à Android. Android a un package de support appelé support-annotations. Cela fournit des dizaines d' annotations spécifiques à Android et fournit également des annotations courantes comme NonNull, Nullableetc.

Pour ajouter un package de support-annotations , ajoutez la dépendance suivante dans votre build.gradle:

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

puis utilisez:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}

5

En attendant que cela soit trié en amont (Java 8?), Vous pouvez également simplement définir votre propre projet local @NotNullet @Nullableannotations. Cela peut également être utile si vous travaillez avec Java SE, où il javax.validation.constraints n'est pas disponible par défaut.

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 {}

Certes, cela serait largement à des fins décoratives ou à l'épreuve du temps, car ce qui précède n'ajoute évidemment pas en soi un support pour l'analyse statique de ces annotations.


4

Si vous développez pour Android, vous êtes quelque peu lié à Eclipse (édition: au moment de la rédaction, plus maintenant), qui a ses propres annotations. Il est inclus dans Eclipse 3.8+ (Juno), mais désactivé par défaut.

Vous pouvez l'activer dans Préférences> Java> Compilateur> Erreurs / Avertissements> Analyse nulle (section repliable en bas).

Cochez "Activer l'analyse nulle basée sur les annotations"

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage a des recommandations sur les paramètres. Cependant, si vous avez des projets externes dans votre espace de travail (comme le SDK facebook), ils peuvent ne pas satisfaire ces recommandations, et vous ne voudrez probablement pas les corriger avec chaque mise à jour du SDK ;-)

J'utilise:

  1. Accès au pointeur nul: erreur
  2. Violation de la spécification nulle: erreur (liée au point n ° 1)
  3. Accès potentiel au pointeur nul: avertissement (sinon le SDK facebook aurait des avertissements)
  4. Conflit entre annotations nulles et inférence nulle: avertissement (lié au point n ° 3)

4
lié à Eclipse? Pas vrai.
dcow

1
@DavidCowden IntelliJ IDEA avec prise en charge d'Android dev`ing, je pense, était disponible quelque temps avant l'introduction d'AndroidStudio.
Mārtiņš Briedis

@ MārtiņšBriedis oui, c'est vrai. Je pense que tu voulais dire @chaqke.
dcow

il convient de noter qu'android et intellij ont des annotations distinctes, et le resteront probablement jusqu'à ce que java inclue des annotations officielles. ce sont des instructions pour utiliser les annotations eclipse avec eclipse.
chaqke

Il n'a jamais été lié à Eclipse. Vous pouvez utiliser n'importe quel IDE que vous souhaitez.
DennisK

4

Si vous travaillez sur un grand projet, vous pouvez être mieux de créer vos propres @Nullable et / ou @NotNullannotations.

Par exemple:

@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 vous utilisez la bonne stratégie de rétention , les annotations ne seront pas disponibles au moment de l'exécution . De ce point de vue, c'est juste une chose interne .

Même si ce n'est pas une science stricte, je pense qu'il est plus logique d'utiliser une classe interne pour cela.

  • C'est une chose interne. (pas d'impact fonctionnel ou technique)
  • Avec de nombreux usages.
  • IDE comme support IntelliJ personnalisés @Nullable/ @NotNullannotations.
  • La plupart des frameworks préfèrent également utiliser leur propre version interne.

Questions supplémentaires (voir commentaires):

Comment configurer cela dans IntelliJ?

Cliquez sur "officier de police" dans le coin inférieur droit de la barre d'état d'IntelliJ. Et cliquez sur "Configurer les inspections" dans la fenêtre contextuelle. Suivant ... configurer les annotations


1
J'ai essayé votre conseil, mais ideane dites rien à propos de void test(@NonNull String s) {}appelé partest(null);
user1244932

3
@ user1244932 Voulez-vous dire IntelliJ IDEA? Vous pouvez configurer les annotations de nullité qu'il utilise pour l'analyse statique. Je ne sais pas exactement où, mais un endroit pour les définir est dans "Fichier> Paramètres> Build, Execution, Deployment> Compiler" et il y a un bouton "Configurer les annotations ...".
Adowrath

@ user1244932 voir la capture d'écran si vous recherchez toujours cela.
bvdb

3

Il y a déjà trop de réponses ici, mais (a) c'est 2019, et il n'y a toujours pas de "standard" Nullableet (b) aucune autre réponse ne fait référence à Kotlin.

La référence à Kotlin est importante, car Kotlin est 100% interopérable avec Java et il a une fonction de sécurité Null de base. Lors de l'appel de bibliothèques Java, il peut tirer parti de ces annotations pour permettre aux outils Kotlin de savoir si une API Java peut accepter ou retourner null.

Pour autant que je sache, les seuls Nullablepackages compatibles avec Kotlin sont org.jetbrains.annotationset android.support.annotation(maintenant androidx.annotation). Ce dernier est uniquement compatible avec Android, il ne peut donc pas être utilisé dans des projets JVM / Java / Kotlin non Android. Cependant, le package JetBrains fonctionne partout.

Donc, si vous développez des packages Java qui devraient également fonctionner dans Android et Kotlin (et être pris en charge par Android Studio et IntelliJ), votre meilleur choix est probablement le package JetBrains.

Maven:

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

Gradle:

implementation 'org.jetbrains:annotations-java5:15.0'

2
Hmm, cela dit le contraire: kotlinlang.org/docs/reference/…
skagedal

3

Il existe une autre façon de le faire en Java 8. Je fais 2 choses pour accomplir ce dont j'avais besoin:

  1. Rendre les champs nullables explicites avec les types en encapsulant les champs nullables avec java.util.Optional
  2. Vérifier que tous les champs non nullables ne sont pas null au moment de la construction avec java.util.Objects.requireNonNull

Exemple:

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);
  }

Donc ma question est, avons-nous même besoin d'annoter lors de l'utilisation de java 8?

Edit: J'ai découvert plus tard que certains considèrent une mauvaise pratique à utiliser Optionaldans les arguments, il y a une bonne discussion avec les avantages et les inconvénients ici Pourquoi Java 8 en option ne devrait-il pas être utilisé dans les arguments

Option alternative étant donné qu'il n'est pas recommandé d'utiliser Facultatif dans les arguments, nous avons besoin de 2 constructeurs:

  //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();
      }

Je dirais que vous avez toujours besoin de l'annotation @NotNull pour les 4 paramètres formels afin que les vérificateurs d'analyses statiques sachent que vous ne devez pas les annuler. Il n'y a encore rien dans le langage Java qui impose cela. Vous devez également vérifier que la description n'est pas nulle si vous programmez de manière défensive.
jaxzin

2
Je peux encore écrire ce code: new Role(null,null,null,null);. Avec les annotations, mon IDE et mon analyse statique avertiront que null ne peut pas être transmis à ces paramètres. Sans cela, je ne le découvrirai pas avant d'avoir exécuté le code. Voilà la valeur des annotations.
jaxzin

2
Je suis également dans des environnements où les développeurs peuvent utiliser n'importe quel IDE ou éditeur de texte qu'ils préfèrent, ce n'est pas mutuellement exclusif. Nous intégrons ensuite le plug-in maven-pmd et / ou SonarQube dans le processus de construction pour encourager et mettre en évidence, et même transmettre, les problèmes de qualité du code avant la fusion, par exemple sur les demandes d'extraction.
jaxzin

2
Facultatif n'est pas destiné à être utilisé comme argument de méthode ou champ privé. Voir par exemple: stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
assylias

1
@assylias oui, je l'ai découvert plus tard, ils disent que ce n'est pas recommandé car cela ne nous achètera rien, je peux certainement comprendre leur rationalité. Dans ce cas, je mets ici, on pourrait rendre l'argument description non nul et le code client pourrait passer une chaîne vide, mais dans de nombreux cas, il pourrait être pratique de distinguer entre une chaîne vide et de ne pas avoir de valeur. Merci pour votre commentaire. Je mettrai à jour la réponse.
Mozart Brocchini

2

Le soleil n'a-t-il pas le leur maintenant? Qu'est-ce que c'est:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

Cela semble être fourni avec toutes les versions de Java que j'ai utilisées au cours des dernières années.

Edit: Comme mentionné dans les commentaires ci-dessous, vous ne voudrez probablement pas les utiliser. Dans ce cas, mon vote est pour les annotations IntelliJ jetbrains!


10
Je n'ai aucune idée de ce que c'est, mais le nom du paquet devrait être un GRAND INDICE qu'il n'est PAS destiné à un usage général.
Stephen C

3
On s'abstient généralement d'utiliser des classes dans l'espace de noms com.sun car elles sont internes; non destiné à un usage direct; et sans garantie quant à leur disponibilité ou comportement futur. Il faut avoir un boîtier vraiment solide pour utiliser directement un artefact com.sun.
luis.espinal

plus quelque chose affiché dans un format HTML si pauvre (sur Java2s.com pour couronner le tout) devrait vous donner quelques drapeaux rouges :)
luis.espinal

2

Une des bonnes choses à propos d'IntelliJ est que vous n'avez pas besoin d'utiliser leurs annotations. Vous pouvez écrire le vôtre ou utiliser ceux de n'importe quel autre outil que vous aimez. Vous n'êtes même pas limité à un seul type. Si vous utilisez deux bibliothèques qui utilisent des annotations @NotNull différentes, vous pouvez dire à IntelliJ de les utiliser toutes les deux. Pour ce faire, allez dans "Configurer les inspections", cliquez sur l'inspection "Conditions constantes et exceptions" et cliquez sur le bouton "Configurer les inspections". J'utilise Nullness Checker partout où je peux, donc j'ai configuré IntelliJ pour utiliser ces annotations, mais vous pouvez le faire fonctionner avec n'importe quel autre outil que vous voulez. (Je n'ai aucune opinion sur les autres outils car j'utilise les inspections d'IntelliJ depuis des années et je les adore.)


1

Une autre option est les annotations fournies avec ANTLR 4. Suite à la demande d'extraction # 434 , l'artefact contenant les annotations @NotNullet @Nullableinclut un processeur d'annotations qui génère des erreurs et / ou des avertissements au moment de la compilation en cas d'utilisation abusive de l'un de ces attributs (par exemple, si les deux sont appliqués au même élément, ou s'il @Nullableest appliqué à un élément de type primitif). Le processeur d'annotations fournit une assurance supplémentaire pendant le processus de développement logiciel que les informations transmises par l'application de ces annotations sont exactes, y compris en cas d'héritage de méthode.


1

Si vous construisez votre application à l'aide de Spring Framework, je suggérerais d'utiliser la validation àjavax.validation.constraints.NotNull partir de Beans Validation empaquetée dans la dépendance suivante:

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

Le principal avantage de cette annotation est que Spring prend en charge à la fois les paramètres de méthode et les champs de classe annotés javax.validation.constraints.NotNull. Tout ce que vous devez faire pour activer le support est:

  1. fournir le pot api pour la validation des beans et le pot avec l'implémentation du validateur des annotations jsr-303 / jsr-349 (qui vient avec la dépendance 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. fournir MethodValidationPostProcessor au contexte de Spring

      @Configuration
      @ValidationConfig
      public class ValidationConfig implements MyService {
    
            @Bean
            public MethodValidationPostProcessor providePostProcessor() {
                  return new MethodValidationPostProcessor()
            }
      }
  3. enfin vous annotez vos classes avec Spring org.springframework.validation.annotation.Validatedet la validation sera automatiquement gérée par Spring.

Exemple:

@Service
@Validated
public class MyServiceImpl implements MyService {

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

Lorsque vous essayez d'appeler la méthode doSomething et que vous passez null comme valeur de paramètre, spring (au moyen de HibernateValidator) lance ConstraintViolationException. Pas besoin de travaux manuels ici.

Vous pouvez également valider les valeurs de retour.

Un autre avantage important du passage javax.validation.constraints.NotNullà Beans Validation Framework est qu'il est encore en cours de développement et que de nouvelles fonctionnalités sont prévues pour la nouvelle version 2.0.

Et alors @Nullable? Il n'y a rien de tel dans Beans Validation 1.1. Eh bien, je pourrais faire valoir que si vous décidez d'utiliser @NotNullque tout ce qui n'est PAS annoté @NonNullest effectivement "annulable", l' @Nullableannotation est donc inutile.


1
S'il-vous-plait, ne l'utilisez pas. Il est utilisé pour la validation d'exécution, PAS pour l'analyse de code statique. Voir justsomejavaguy.blogspot.com/2011/08/… pour plus de détails. Source: réponse SUPPRIMÉE avec 219 votes par @ luis.espinal.
koppor

@koppor: Je ne suis pas d'accord. Si cela n'est pas destiné à être utilisé, pourquoi Spring le gérerait-il au moment de l'exécution? Le framework de validation Beans permet également de créer des annotations uniquement pour l'analyse à l'exécution, car il permet d'accéder à l'objet Context (actuellement annoté / validé instancje) lors de l'exécution.
walkeros

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.