Question Quelle annotation Java @NotNull devrais-je utiliser?


Je cherche à rendre mon code plus lisible et à utiliser des outils tels que l'inspection de code IDE et / ou l'analyse de code statique (FindBugs et Sonar) pour éviter les exceptions NullPointer. Beaucoup d'outils semblent incompatibles entre eux @NotNull/@NonNull/@Nonnull annotation et la liste de tous les dans mon code serait terrible à lire. Des suggestions de quel est le «meilleur»? Voici la liste des annotations équivalentes que j'ai trouvées:

  • javax.validation.constraints.NotNull
    Créé pour la validation d'exécution, pas d'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 aussi fonctionner avec Findbugs, mais JSR-305 est inactif. (Voir également: Quel est le statut de JSR 305?) la 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 Projet Lombok.
    Annotation d'espace réservé car il n'y a pas de standard.
    la source, Documentation

  • android.support.annotation.NonNull
    Marker annotation disponible sur Android, fourni par le package support-annotations
    Documentation

  • org.eclipse.jdt.annotation.NonNull
    Utilisé par Eclipse pour l'analyse de code statique
    Documentation


739
2018-02-10 22:05


origine


Réponses:


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

Je ne suis pas affilié avec le groupe qui offre le logiciel, mais je suis un fan.

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

  1. Il a un vérificateur de défauts pour nullité (@Nullable), mais a aussi pour immutabilité et interner (et d'autres). J'utilise le premier (nullité) et j'essaie d'entrer dans le second (immutabilité / IGJ). J'essaie le troisième, mais je ne suis pas certain de l'utiliser à long terme pour le moment. 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 permettant d'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-null sauf les locaux (NNEL). Fondamentalement, cela signifie que par défaut, le vérificateur traite everyhing (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. Par 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 cadre vous permet d'utiliser avec sans créer de dépendance sur le framework en insérant vos annotations dans un commentaire: par ex. /*@Nullable*/. C'est bien parce que vous pouvez annoter et vérifier une bibliothèque ou un code partagé, mais être capable d'utiliser cette bibliothèque / partagée codée dans un autre projet qui n'utilise pas le framework. C'est une fonctionnalité intéressante. J'ai pris l'habitude de l'utiliser, même si j'ai tendance à activer le Checker Framework sur tous mes projets maintenant.

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


78
2018-02-10 22:59



Depuis Oracle décidé de ne pas normaliser @NonNull (et @Nullable) pour l'instant, 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 autre 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.constraints ou javax.annotation. Le premier vient avec JEE. Si cela vaut mieux que javax.annotation, ce qui pourrait éventuellement arriver avec JSE ou jamais, c'est un sujet de débat. Personnellement, je préfère javax.annotation parce que je ne voudrais pas la dépendance JEE.

Cela nous laisse avec

javax.annotation

qui est aussi le plus court.

Il n'y a qu'une seule syntaxe qui serait encore meilleure: java.annotation.Nullable. Comme d'autres paquets ont été gradués de javax à java dans le passé, le javax.annotation serait être un pas dans la bonne direction.

la mise en oeuvre

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

D'abord pour les similitudes:

Les annotations @NonNull ont toutes la ligne

public @interface NonNull {}

à l'exception de

  • org.jetbrains.annotations qui l'appelle @NotNull et a une implémentation triviale
  • javax.annotation qui a une implémentation plus longue
  • javax.validation.constraints qui l'appelle aussi @NotNull et a une implémentation

Les annotations @Nullable ont toutes la ligne

public @interface Nullable {}

sauf pour (à nouveau) les org.jetbrains.annotations avec leur implémentation triviale.

Pour les différences:

Un frappant est celui

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

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

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

sont seulement l'heure de compilation (@Retention (CLASS)).

Comme décrit dans cette réponse SO l'impact des annotations d'exécution est plus petit qu'on pourrait le penser, mais ils ont l'avantage des outils permettant de faire des contrôles d'exécution en plus de la compiler les temps.

Une autre différence importante est  dans le code, les annotations peuvent être utilisées. Il y a deux approches différentes. Certains paquets utilisent des contextes de style JLS 9.6.4.1. Le tableau suivant donne un aperçu:


                                PARAMÈTRE DE LA MÉTHODE DE CHAMP 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 et org.checkerframework.checker.nullness.qual utilisent les contextes définis dans JLS 4.11, 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, je liste le code de chaque annotation ci-dessous. Pour faciliter la comparaison, j'ai supprimé les commentaires, les importations et l'annotation @Documented. (ils avaient tous @Documented à l'exception des classes du package Android). J'ai réorganisé les lignes et les champs @Target 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 implémentations de @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 {}

Les deux paquets suivants n'ont pas de @Nullable, donc je les liste séparément Lombok a un peu ennuyeux @NonNull. Dans javax.validation.constraints, @NonNull est en fait un @NotNull et il a une mise en œuvre 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

Former mon expérience javax.annotation est au moins supporté par Eclipse et le Checker Framework hors de la boîte.

Résumé

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

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

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


Sources

  • android.support.annotation à partir de android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations à partir de findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation depuis 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
  • lombok de lombok commenter f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints de validation-api-1.0.0.GA-sources.jar

60
2018-03-09 12:20



J'utilise celui d'IntelliJ, parce que je m'intéresse surtout à IntelliJ signalant des choses qui pourraient produire un NPE. Je suis d'accord qu'il est frustrant de ne pas avoir d'annotation standard dans le JDK. On parle de l'ajouter, il pourrait en faire Java 7. Dans ce cas, il y aura un autre à choisir!


49
2018-02-10 22:09



Selon le Liste des fonctionnalités de Java 7 Les annotations de type JSR-308 sont différées vers Java 8. Les annotations JSR-305 ne sont même pas mentionnées.

Il y a un peu d'information sur l'état de JSR-305 dans un annexe du dernier projet 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 supportés 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 ressemble vraiment à Java 8, il ne me surprendrait pas si l'intérêt pour JSR-305 revivait. AFAIK, l'équipe JSR-305 n'a pas officiellement abandonné son travail. Ils viennent d'être tranquilles pendant 2+ ans.

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


31
2018-02-10 22:21



Pour les projets Android, vous devez utiliser android.support.annotation.NonNull et android.support.annotation.Nullable. Ces annotations utiles pour Android sont disponibles dans le Bibliothèque de support.

De http://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érifiez déjà votre code et signalez les problèmes potentiels sur la base de ces   annotations.


25
2017-09-05 09:51



JSR305 et FindBugs sont créés par la même personne. Les deux sont mal entretenus mais sont aussi standard que possible et sont supportés par tous les principaux IDE. Les bonnes nouvelles sont qu'elles fonctionnent bien en l'état.

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;

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

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

Ça ressemble à JSR 308  est faire en JDK 8 et bien que la JSR ne définit pas @NotNull, le accompagnant Checkers Framework Est-ce que. Au moment d'écrire ces lignes, le plugin Maven est inutilisable à cause de ce bug: https://github.com/typetools/checker-framework/issues/183


16
2017-11-23 02:52



Si quelqu'un cherche simplement les classes IntelliJ: vous pouvez les obtenir depuis le dépôt maven avec

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

16
2018-04-25 14:15



Eclipse a aussi ses propres annotations.

org.eclipse.jdt.annotation.NonNull

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


11
2017-09-17 11:10



Juste en soulignant que l'API Java Validation (javax.validation.constraints.*) ne vient pas avec @Nullable annotation, 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.


10
2017-08-01 12:07



Malheureusement, JSR 308 ne va pas ajouter plus de valeurs que ce projet local suggestion non null ici

Java 8 ne viendra pas avec une seule annotation par défaut ou la sienne Checker cadre. Similaire à Find-bugs ou JSR 305, ce JSR est mal entretenu par un petit groupe d'équipes principalement académiques.

Aucun pouvoir commercial derrière, donc JSR 308 lancements EDR 3 (Examen préliminaire préliminaire à JCP) MAINTENANT, tandis que Java 8 est supposé expédier en moins de 6 mois: -O Semblable à 310 BTW mais contrairement à 308 Oracle a pris en charge de 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 Framework et JSR 308 créera sa propre annotation de vérificateur propriétaire.

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 9 ou 10ou via des frameworks comme Apache Commons ou Google Guava;-)


6
2018-04-01 09:55