web-dev-qa-db-fra.com

Quel est l'intérêt de vérifier la goyaveNotNull

Je suis assez nouveau dans Guava (soyons honnêtes, je ne suis pas "assez nouveau", je suis un débutant à ce sujet) et j'ai donc décidé de passer en revue certains documents et je suis assez étonné en lisant ceci:

com.google.common.base.Preconditions.checkNotNull(...)

Je ne comprends pas le but de cette méthode. Cela signifie qu'au lieu de faire:

myObject.getAnything();

(ce qui pourrait causer un NullPointerException si myObject est null)

Je devrais utiliser

checkNotNull(myObject).getAnything();

qui lancera un NullPointerException si myObject est nul et renvoie myObject s'il n'est pas nul .

Je suis perplexe et c'est peut-être la question la plus stupide de tous les temps mais ...

Quel est le but de ceci? Ces deux lignes font exactement la même chose que pour les résultats dans toutes les situations auxquelles je peux penser.

Je ne pense même pas que ce dernier est plus lisible.

Donc je dois manquer quelque chose. Qu'Est-ce que c'est?

61
Ar3s

L'idée est d'échouer vite. Par exemple, considérons cette classe idiote:

public class Foo {
    private final String s;

    public Foo(String s) {
        this.s = s;
    }

    public int getStringLength() {
        return s.length();
    }
}

Supposons que vous ne souhaitiez pas autoriser les valeurs NULL pour s. (ou bien getStringLength jettera un NPE). Avec la classe telle quelle, au moment où vous attrapez cette null, il est trop tard - il est très difficile de savoir qui l'a mise là. Le coupable pourrait bien appartenir à une classe totalement différente et cette instance Foo aurait pu être construite il y a longtemps. Maintenant, vous devez passer au peigne fin sur votre base de code pour savoir qui aurait pu y placer une valeur null.

Au lieu de cela, imaginez ce constructeur:

public Foo(String s) {
    this.s = checkNotNull(s);
}

Maintenant, si quelqu'un met un null dedans, vous découvrirez tout de suite - et vous aurez la trace de la pile qui vous indique exactement l'appel qui a mal tourné.


Une autre fois, cela peut être utile si vous souhaitez vérifier les arguments avant de prendre des mesures susceptibles de modifier un état. Par exemple, considérons cette classe qui calcule la moyenne de toutes les longueurs de chaîne obtenues:

public class StringLengthAverager {
    private int stringsSeen;
    private int totalLengthSeen;

    public void accept(String s) {
        stringsSeen++;
        totalLengthSeen += s.length();
    }

    public double getAverageLength() {
        return ((double)totalLengthSeen) / stringsSeen;
    }
}

L'appel de accept(null) provoquera l'envoi d'un NPE, mais pas avant que stringsSeen n'ait été incrémenté. Cela peut ne pas être ce que vous voulez; en tant qu'utilisateur de la classe, je peux m'attendre à ce que, si elle n'accepte pas les valeurs NULL, son état doit rester inchangé si vous passez une valeur NULL (en d'autres termes: l'appel doit échouer, mais l'objet ne doit pas être invalidé). Évidemment, dans cet exemple, vous pouvez également résoudre ce problème en obtenant s.length() avant d’incrémenter stringsSeen, mais vous pouvez voir comment, pour une méthode plus longue et plus complexe, il peut être utile de vérifier au préalable que tous de vos arguments sont valides, et alors seulement modifier l'état:

    public void accept(String s) {
        checkNotNull(s); // that is, s != null is a precondition of the method

        stringsSeen++;
        totalLengthSeen += s.length();
    }
91
yshavit

myObject.getAnything(); (ce qui peut provoquer une exception NullPointerException si myObject est null)

Non ... il va lancer NPE chaque fois que myObject == null. En Java, il n’ya aucune chance d’appeler une méthode avec null receiver (une exception théorique sont les méthodes statiques, mais elles peuvent et doivent toujours être appelées sans objet).


Je devrais utiliser checkNotNull(myObject).getAnything();

Non tu ne devrais pas. Ce serait plutôt redondant ( Update ).

Vous devez utiliser checkNotNull pour échouer rapidement. Sans elle, vous pouvez passer un null illégal à une autre méthode, qui le transmet plus loin, et ainsi de suite, où il échoue. Ensuite, vous pouvez avoir besoin de chance pour savoir qu’en fait, la toute première méthode aurait dû refuser null.


La réponse de yshavit mentionne un point important: transmettre une valeur illégale est une mauvaise chose, mais la stocker et la transmettre ultérieurement est encore pire.

Mise à jour

Réellement,

 checkNotNull(myObject).getAnything()

logique, car vous exprimez clairement votre intention de ne pas accepter les valeurs nulles. Sans cela, quelqu'un pourrait penser que vous avez oublié le chèque et le convertir en quelque chose comme

 myObject != null ? myObject.getAnything() : somethingElse

OTOH, je ne pense pas que le chèque vaut la verbosité. Dans un meilleur langage , le système de types considérerait la nullabilité et nous donnerait du sucre sémantique comme

 myObject!!.getAnything()                    // checkNotNull
 myObject?.getAnything()                     // safe call else null
 myObject?.getAnything() ?: somethingElse    // safe call else somethingElse

pour nullable myObject, alors que la syntaxe à points standard ne serait autorisée que lorsque l'on sait que myObject est non nul.

9
maaartinus

J'ai lu tout ce fil il y a quelques minutes. Néanmoins, je ne comprenais pas pourquoi devrions-nous utiliser checkNotNull. Ensuite, jetez un coup d'œil sur la documentation de la classe de condition préalable de Guava et j'ai obtenu ce à quoi je m'attendais. Une utilisation excessive de checkNotNull dégradera définitivement les performances.

Ma pensée est que la méthode checkNotNull vaut la peine d'être utilisée pour la validation des données qui provient directement de l'utilisateur ou de l'API de fin à l'interaction utilisateur. Il ne doit pas être utilisé dans chacune des méthodes de l'API interne, car vous ne pouvez pas arrêter exception au lieu de corriger votre API interne pour éviter Exception.

Selon DOC: Lien

Utilisation de checkNotNull:

public static double sqrt(double value) {
     Preconditions.checkArgument(value >= 0.0, "negative value: %s", value);
     // calculate the square root
}

Avertissement sur les performances

Le but de cette classe est d’améliorer la lisibilité du code, mais dans certaines circonstances, cela peut entraîner un coût de performance important. N'oubliez pas que les valeurs de paramètre pour la construction de message doivent toutes être calculées avec impatience, et que la création de tableaux de type auto-sélection et varargs peut également se produire, même lorsque la vérification de la condition préalable réussit (comme cela devrait presque toujours être le cas en production). Dans certaines circonstances, ces cycles et affectations de processeur gaspillés peuvent s’ajouter à un problème réel. Les contrôles préalables sensibles aux performances peuvent toujours être convertis au format habituel:

if (value < 0.0) {
     throw new IllegalArgumentException("negative value: " + value);
}
4
mahbub.kuet