web-dev-qa-db-fra.com

Comment utiliser les annotations @Nullable et @Nonnull plus efficacement?

Je peux voir que @Nullable et @Nonnull annotations pourraient être utiles dans la prévention de NullPointerExceptions mais ils ne se propagent pas très loin.

  • L'efficacité de ces annotations disparaît complètement après un niveau d'indirection, donc si vous n'en ajoutez que quelques-unes, elles ne se propagent pas très loin.
  • Étant donné que ces annotations ne sont pas correctement appliquées, le risque de prendre une valeur marquée avec @Nonnull n'est pas nul et par conséquent de ne pas effectuer de contrôles nuls.

Le code ci-dessous fait en sorte qu'un paramètre marqué avec @Nonnull soit null sans soulever de réclamations. Il jette un NullPointerException quand il est exécuté.

public class Clazz {
    public static void main(String[] args){
        Clazz clazz = new Clazz();

        // this line raises a complaint with the IDE (IntelliJ 11)
        clazz.directPathToA(null);

        // this line does not
        clazz.indirectPathToA(null); 
    }

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }
}

Existe-t-il un moyen de mieux appliquer ces annotations et/ou de les diffuser davantage?

111
Mike Rylander

Réponse courte: je suppose que ces annotations ne sont utiles que pour que votre IDE vous avertisse d'erreurs de pointeur potentiellement nulles.

Comme indiqué dans le livre "Clean Code", vous devriez vérifier les paramètres de votre méthode publique et éviter de vérifier les invariants.

Un autre bon conseil est de ne jamais renvoyer de valeur NULL, mais d'utiliser Null Object Pattern .

54
Pedro Boechat

A côté de vous IDE vous donnant des indices que vous passez null là où on s'attend à ce qu'il ne soit pas nul, vous avez d'autres avantages:

  • Les outils d'analyse de code statique peuvent tester la même chose que votre IDE (par exemple, FindBugs)
  • Vous pouvez utiliser AOP pour vérifier ces assertions

Il peut donc être utile de créer un code plus facile à gérer (vous n'avez pas besoin de vérifications null) et moins sujet aux erreurs.

23
Uwe Plonus

En compilant l'exemple original dans Eclipse à la conformité 1.8 et avec l'analyse des annotations basée sur les annotations activée, nous obtenons cet avertissement:

    directPathToA(y);
                  ^
Null type safety (type annotations): The expression of type 'Integer' needs unchecked conversion to conform to '@NonNull Integer'

Cet avertissement est libellé par analogie avec les avertissements que vous obtenez lorsque vous mélangez du code généré avec du code hérité à l'aide de types bruts ("conversion non contrôlée"). Nous avons exactement la même situation ici: la méthode indirectPathToA() a une signature "héritée" en ce sens qu'elle ne spécifie aucun contrat nul. Les outils peuvent facilement le signaler, ils vont donc vous chasser dans toutes les allées où des annotations nulles doivent être propagées mais ne le sont pas encore.

Et lorsque vous utilisez un @NonNullByDefault intelligent, nous n’avons même pas à le dire à chaque fois.

En d'autres termes: le fait que les annotations nulles "propagent très loin" puisse dépendre de l'outil que vous utilisez et de la rigueur avec laquelle vous tenez compte de tous les avertissements émis par l'outil. Avec TYPE_USE null annotations , vous avez enfin la possibilité de laisser l'outil vous avertir à propos de tous les possibles dans votre programme, car la valeur NULL est devenu une propriété intrinsèque du système de types.

11
Stephan Herrmann

Je pense que cette question initiale renvoie indirectement à une recommandation générale selon laquelle la vérification du point zéro au moment de l'exécution est toujours nécessaire, même si @NonNull est utilisé. Reportez-vous au lien suivant:

nouvelles annotations de types de Java 8

Dans le blog ci-dessus, il est recommandé que:

Les annotations de type facultatives ne remplacent pas la validation à l'exécution Avant les annotations de type, l'emplacement principal pour décrire des éléments tels que la nullabilité ou les plages était dans le javadoc. Avec les annotations de type, cette communication entre dans le bytecode de manière à permettre une vérification à la compilation. Votre code doit toujours effectuer la validation à l'exécution.

10
jonathanzh

Je conviens que les annotations "ne se propagent pas très loin". Cependant, je vois l'erreur du côté du programmeur.

Je comprends l'annotation Nonnull comme documentation. La méthode suivante exprime qu'il nécessite (à titre de condition préalable) un argument non nul x.

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }

L'extrait de code suivant contient alors un bogue. La méthode appelle directPathToA() sans imposer que y est non nulle (en d'autres termes, elle ne garantit pas la condition préalable de la méthode appelée). Une possibilité consiste à ajouter une annotation Nonnull à indirectPathToA() (propagation de la condition préalable). La deuxième possibilité consiste à vérifier la nullité de y dans indirectPathToA() et à éviter l'appel à directPathToA() lorsque y est nul.

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }
6
Andres

Ce que je fais dans mes projets consiste à activer l'option suivante dans l'inspection du code "Conditions constantes et exceptions":
Suggère une annotation @Nullable pour les méthodes pouvant renvoyer null et signaler les valeurs nullables transmises à des paramètres non annotés Inspections

Lorsqu'il est activé, tous les paramètres non annotés seront traités comme des valeurs non nulles. Un avertissement apparaîtra donc également sur votre appel indirect:

clazz.indirectPathToA(null); 

Pour des vérifications encore plus fortes, Checker Framework peut être un bon choix (voir ce Nice tutorial .
Note : Je ne l'ai pas encore utilisé et il peut y avoir des problèmes avec le compilateur Jack: voir this bugreport =

4
TmTron

Dans Java, j'utiliserais type facultatif de Guava . Étant un type réel, vous obtenez des garanties du compilateur quant à son utilisation. Il est facile de la contourner et d’obtenir une NullPointerException, mais au moins la signature de la méthode indique clairement ce qu’elle attend en tant qu’argument ou ce qu’elle pourrait renvoyer.

3
Ionuț G. Stan

Si vous utilisez Kotlin, il prend en charge ces annotations de nullabilité dans son compilateur et vous empêchera de transmettre une valeur null à une méthode Java nécessitant un argument non nul. Bien que cette question visait à l'origine Java, je mentionne cette fonctionnalité Kotlin, car elle est spécifiquement ciblée sur ces annotations Java et question était "Y a-t-il un moyen de rendre ces annotations plus strictement appliquées et/ou de les propager davantage?" et cette fonctionnalité ne rend ces annotations plus strictement appliquées .

Classe Java utilisant l'annotation @NotNull

public class MyJavaClazz {
    public void foo(@NotNull String myString) {
        // will result in an NPE if myString is null
        myString.hashCode();
    }
}

La classe Kotlin appelle la classe Java et transmet la valeur null pour l'argument annoté avec @NotNull

class MyKotlinClazz {
    fun foo() {
        MyJavaClazz().foo(null)
    }
}  

Erreur du compilateur Kotlin lors de l'application de l'annotation @NotNull.

Error:(5, 27) Kotlin: Null can not be a value of a non-null type String

voir: http://kotlinlang.org/docs/reference/Java-interop.html#nullability-annotations

3
Mike Rylander