Une méthode de récupération doit-elle renvoyer 'null' ou générer une exception lorsqu'elle ne peut pas produire la valeur de retour? [fermé]

StackOverflow https://stackoverflow.com/questions/175532

  •  05-07-2019
  •  | 
  •  

Question

J'ai une méthode supposée renvoyer un objet s'il est trouvé.

S'il n'est pas trouvé, devrais-je:

  1. retourne la valeur null
  2. lève une exception
  3. autre
Était-ce utile?

La solution

Si vous vous attendez toujours à trouver une valeur, lancez l'exception si elle est manquante. L'exception signifierait qu'il y a un problème.

Si la valeur peut être manquante ou présente et que les deux sont valides pour la logique d'application, renvoyez une valeur null.

Plus important: que faites-vous d'autres endroits dans le code? La cohérence est importante.

Autres conseils

Ne lève une exception que s'il s'agit vraiment d'une erreur. Si le comportement de l’objet n’existe pas, renvoyez la valeur null.

Sinon, c'est une question de préférence.

En règle générale, si la méthode doit toujours renvoyer un objet, utilisez l'exception. Si vous prévoyez le null occasionnel et que vous voulez le gérer d'une certaine manière, optez pour le null.

Quoi que vous fassiez, je déconseille fortement la troisième option: renvoyer une chaîne qui dit "WTF".

Si null n'indique jamais d'erreur, il suffit de renvoyer null.

Si la valeur null est toujours une erreur, une exception est alors générée.

Si null est parfois une exception, codez deux routines. Une routine lève une exception et l’autre est une routine de test booléen qui renvoie l’objet dans un paramètre de sortie et la valeur false si l’objet n’a pas été trouvé.

Il est difficile d’abuser de la routine Try. Il est très facile d'oublier de vérifier la valeur null.

Donc, quand null est une erreur, il vous suffit d'écrire

object o = FindObject();

Lorsque le null n'est pas une erreur, vous pouvez coder quelque chose comme

if (TryFindObject(out object o)
  // Do something with o
else
  // o was not found

Je voulais juste récapituler les options mentionnées précédemment, en en ajoutant de nouvelles:

  1. retourne la valeur null
  2. jette une exception
  3. utiliser le modèle d'objet null
  4. fournit un paramètre booléen à votre méthode afin que l'appelant puisse choisir s'il souhaite que vous leviez une exception
  5. fournit un paramètre supplémentaire pour que l'appelant puisse définir une valeur qu'il récupère s'il ne trouve aucune valeur

Ou vous pouvez combiner ces options:

Fournissez plusieurs versions surchargées de votre getter afin que l'appelant puisse choisir la voie à suivre. Dans la plupart des cas, seul le premier dispose d’une implémentation de l’algorithme de recherche et les autres ne font qu’envelopper le premier:

Object findObjectOrNull(String key);
Object findObjectOrThrow(String key) throws SomeException;
Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject);
Object findObjectOrDefault(String key, Object defaultReturnValue);

Même si vous choisissez de ne fournir qu'une seule implémentation, vous pouvez utiliser une convention de dénomination comme celle-ci pour clarifier votre contrat. Cela vous aidera également si vous décidez d'ajouter d'autres implémentations.

Vous ne devriez pas en abuser, mais cela peut être utile, en particulier lorsque vous écrivez une classe d'assistance que vous utiliserez dans des centaines d'applications différentes avec de nombreuses conventions de traitement des erreurs.

Utilisez le modèle d'objet null ou émettez une exception.

Soyez cohérent avec les API que vous utilisez.

Demandez-vous simplement: "est-ce un cas exceptionnel que l'objet ne soit pas trouvé"? Si cela devrait se produire dans le cours normal de votre programme, vous ne devriez probablement pas lever d’exception (car ce comportement n’est pas exceptionnel).

Version courte: utilisez des exceptions pour gérer un comportement exceptionnel, mais pas pour gérer un flux de contrôle normal dans votre programme.

-Alan.

Cela dépend si votre langue et votre code promeuvent: LBYL (regarde avant de sauter) ou EAFP (il est plus facile de demander pardon que l'autorisation)

LBYL dit que vous devriez vérifier les valeurs (donc renvoyer une valeur null)
EAFP dit d'essayer simplement l'opération et de voir si elle échoue (lève une exception)

Bien que je sois d’accord avec ce qui précède, les exceptions doivent être utilisées pour les conditions exceptionnelles / d’erreur, et renvoyer une valeur nulle est préférable lors de l’utilisation de chèques.

EAFP contre LBYL en Python:
http://mail.python.org/pipermail/python-list /2003-May/205182.html ( Archive Web )

Avantages de générer une exception:

  1. Un flux de contrôle plus propre dans votre code d'appel. La vérification de la valeur null injecte une branche conditionnelle gérée de manière native par try / catch. La vérification de la valeur null n'indique pas ce que vous recherchez. Recherchez-vous la valeur null parce que vous recherchez une erreur que vous attendez ou recherchez-vous la valeur null afin de ne pas la transmettre plus loin dans la chaîne descendante ?
  2. Supprime l'ambiguïté de ce que " null " signifie. Null est-il représentatif d'une erreur ou est-il null ce qui est réellement stocké dans la valeur? Difficile à dire quand vous n’avez qu’une chose sur laquelle baser cette détermination.
  3. Amélioration de la cohérence du comportement des méthodes dans une application. Les signatures de méthode exposent généralement les exceptions, ce qui vous permet de mieux comprendre les cas où les méthodes d'un compte d'application et les informations que votre l'application peut réagir de manière prévisible.

Pour plus d'explications avec des exemples, voir: http : //metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/

Les exceptions sont liées à la conception par contrat.

L’interface d’un objet est en réalité un contrat entre deux objets, l’appelant doit respecter le contrat ou le destinataire peut échouer avec une exception. Il y a deux contrats possibles

1) toute la méthode entrée est valide, auquel cas vous devez retourner null quand l'objet n'est pas trouvé.

2) seule une entrée est valide, c'est-à-dire celle qui aboutit à un objet trouvé. Dans ce cas, vous DEVEZ proposer une seconde méthode permettant à l'appelant de déterminer si son entrée sera correcte. Par exemple

is_present(key)
find(key) throws Exception

SI ET SEULEMENT SI vous fournissez les deux méthodes du 2e contrat, vous êtes autorisé à lancer une exception si rien n'est trouvé!

Je préfère simplement renvoyer un null et m'appuyer sur l'appelant pour le gérer correctement. L'exception (faute d'un meilleur mot) est si je suis absolument "certain" que cette méthode retournera un objet. Dans ce cas, un échec est une exception et devrait jeter.

Cela dépend de ce que cela signifie que l'objet n'est pas trouvé.

S'il s'agit d'une situation normale, renvoyez la valeur null. Ceci est juste quelque chose qui peut arriver de temps en temps, et les appelants doivent le vérifier.

S'il s'agit d'une erreur, alors déclenchez une exception. Les appelants doivent décider quoi faire avec la condition d'erreur d'objet manquant.

En fin de compte, l’un ou l’autre fonctionnerait, bien que la plupart des gens considèrent généralement qu’une bonne pratique consiste à n’utiliser d’Exceptions que lorsque quelque chose d’exceptionnel s’est produit.

Voici quelques suggestions supplémentaires.

Si vous renvoyez une collection, évitez de renvoyer null, renvoyez une collection vide qui facilite le traitement de l'énumération sans vérification préalable.

Plusieurs API .NET utilisent le modèle d'un paramètre thrownOnError qui donne à l'appelant le choix de savoir s'il s'agit vraiment d'une situation exceptionnelle ou non si l'objet n'est pas trouvé. Type.GetType en est un exemple. Un autre modèle commun avec BCL est le modèle TryGet dans lequel un booléen est renvoyé et la valeur transmise via un paramètre de sortie.

Vous pouvez également envisager le motif Null Object dans certaines circonstances, qui peut être une version par défaut ou une version sans comportement. La clé est d'éviter les contrôles nuls dans la base de code. Voir ici pour plus d'informations http://geekswithblogs.net/dsellers/archive /2006/09/08/90656.aspx

Dans certaines fonctions, j'ajoute un paramètre:

..., bool verify = true)

Vrai signifie jeter, faux signifie retourner une valeur de retour d'erreur. De cette façon, quiconque utilise cette fonction a les deux options. La valeur par défaut doit être vraie, pour le bénéfice de ceux qui oublient la gestion des erreurs.

Renvoyez une valeur null au lieu de générer une exception et indiquez clairement la possibilité d'une valeur de retour null dans la documentation de l'API. Si le code d'appel n'honore pas l'API et ne recherche pas la casse, cela entraînera probablement une sorte d '"exception de pointeur null". de toute façon:)

En C ++, je peux penser à 3 types différents de configuration d’une méthode qui trouve un objet.

Option A

Object *findObject(Key &key);

Renvoie la valeur null lorsqu'un objet est introuvable. Sympa et simple. J'irais avec celui-ci. Les approches alternatives ci-dessous sont destinées aux personnes qui ne détestent pas les extra-param.

Option B

void findObject(Key &key, Object &found);

Transmettez une référence à la variable qui recevra l'objet. La méthode lève une exception lorsqu'un objet est introuvable. Cette convention est probablement plus appropriée si aucun objet ne doit être trouvé, c'est pourquoi vous générez une exception pour indiquer qu'il s'agit d'un cas inattendu.

Option C

bool findObject(Key &key, Object &found);

La méthode retourne false lorsqu'un objet est introuvable. L’avantage de cette option A est que vous pouvez rechercher le cas d’erreur en une seule étape:

if (!findObject(myKey, myObj)) { ...

se référant uniquement au cas où null n’est pas considéré comme un comportement exceptionnel, je suis définitivement pour la méthode try, c’est clair, il n’est pas nécessaire de "lire le livre". ou "regardez avant de sauter" comme on dit ici

donc en gros:

bool TryFindObject(RequestParam request, out ResponseParam response)

et cela signifie que le code de l'utilisateur sera également clair

...
if(TryFindObject(request, out response)
{
  handleSuccess(response)
}
else
{
  handleFailure()
}
...

S'il est important que le code client connaisse la différence entre trouvé et non trouvé et qu'il s'agisse d'un comportement de routine, il est préférable de renvoyer null. Le code client peut alors décider quoi faire.

En règle générale, il devrait renvoyer null. Le code appelant la méthode devrait décider de lever une exception ou d’essayer autre chose.

Ou renvoyer une option

Une option est essentiellement une classe de conteneur qui oblige le client à gérer les cas de stand. Scala a ce concept, recherchez son API.

Ensuite, vous avez des méthodes comme T getOrElse (T valueIfNull) sur cet objet qui renvoient soit l'objet trouvé, soit un élément alternatif spécifié par le client.

Malheureusement, JDK est incohérent. Si vous essayez d'accéder à une clé non existante dans le groupe de ressources, vous obtenez une exception introuvable et lorsque vous demandez une valeur à la carte, vous obtenez la valeur null si elle n'existe pas. Je changerais donc la réponse du gagnant à ce qui suit, si la valeur trouvée peut être null, puis déclenche une exception si elle n’est pas trouvée, sinon je retourne null. Suivez donc la règle avec une exception. Si vous avez besoin de savoir pourquoi la valeur n’est pas trouvée, générez toujours une exception, ou ..

Tant qu'il est supposé renvoyer une référence à l'objet, renvoyer une valeur NULL devrait être une bonne chose.

Cependant, si le résultat est totalement sanglant (comme en C ++, vous devez: 'return blah;' au lieu de 'return & blah;' (ou 'blah' est un pointeur), vous ne pouvez pas retourner. une NULL, parce que ce n'est pas du type 'objet'. Dans ce cas, le fait de lever une exception ou de retourner un objet vide pour lequel aucun indicateur de réussite n'est défini est le meilleur moyen d'aborder le problème.

Ne pensez pas que quiconque a mentionné les frais généraux liés à la gestion des exceptions - nécessite des ressources supplémentaires pour charger et traiter l'exception. Ainsi, à moins que ce ne soit un véritable événement de destruction d'application ou d'arrêt de processus (à l'avenir, cela causerait plus de tort que de bien). J'opterais pour renvoyer une valeur que l'environnement appelant peut interpréter à sa guise.

Je suis d’accord avec ce qui semble être le consensus ici (renvoyer null si "introuvable" est un résultat normal, ou jette une exception si la sémantique de la situation exige que l’objet soit toujours trouvé).

Il existe cependant une troisième possibilité qui pourrait avoir un sens en fonction de votre situation particulière. Votre méthode peut renvoyer un objet par défaut de type "non trouvé". condition permettant au code d’appel de s’assurer qu’il recevra toujours un objet valide sans qu’il soit nécessaire de vérifier la valeur NULL ou de capturer les exceptions.

Renvoie un null, les exceptions sont exactement cela: quelque chose que votre code ne fait pas.

Les

exceptions doivent être exceptionnelles . Renvoie la valeur null s'il est valide de renvoyer la valeur null .

Préfère renvoyer null -

Si l'appelant l'utilise sans vérifier, l'exception se produit quand même.

Si l'appelant ne l'utilise pas vraiment, ne lui imposez pas de essayer / catch , bloc

Si la méthode retourne une collection, retournez une collection vide (comme indiqué ci-dessus). Mais s'il vous plaît pas Collections.EMPTY_LIST ou tel! (en cas de Java)

Si la méthode récupère un seul objet, vous avez quelques options.

  1. Si la méthode doit toujours trouver le résultat et que le fait de ne pas trouver l'objet constitue un cas exceptionnel, vous devez alors lever une exception (en Java: veuillez indiquer une exception non cochée)
  2. (Java uniquement) Si vous pouvez tolérer que la méthode lève une exception vérifiée, lancez une exception ObjectNotFoundException spécifique au projet ou similaire. Dans ce cas, le compilateur vous dit si vous oubliez de gérer l'exception. (Ceci est ma gestion préférée des objets non trouvés en Java.)
  3. Si vous dites que c'est vraiment correct, si l'objet n'est pas trouvé et que le nom de votre méthode ressemble à findBookForAuthorOrReturnNull (..), vous pouvez alors renvoyer null. Dans ce cas, il est vivement recommandé d'utiliser une sorte de vérification statique ou de vérification du compilateur, qui empêche la déréférencement du résultat sans vérification nulle. Dans le cas de Java, cela peut être par exemple. FindBugs (voir DefaultAnnotation à l'adresse http://findbugs.sourceforge.net/manual/annotations.html) ou IntelliJ-Checking.

Faites attention si vous décidez de renvoyer un null. Si vous n'êtes pas le seul programmeur du projet, vous obtiendrez NullPointerExceptions (en Java ou dans une autre langue) au moment de l'exécution! Donc, ne retournez pas les NULL qui ne sont pas vérifiés à la compilation.

Si vous utilisez une bibliothèque ou une autre classe qui lève une exception, vous devez la la renvoyer . Voici un exemple. Example2.java est comme une bibliothèque et Example.java utilise son objet. Main.java est un exemple pour gérer cette exception. Vous devriez montrer un message significatif et (si nécessaire) une trace de pile à l'utilisateur côté appelant.

Main.java

public class Main {
public static void main(String[] args) {
    Example example = new Example();

    try {
        Example2 obj = example.doExample();

        if(obj == null){
            System.out.println("Hey object is null!");
        }
    } catch (Exception e) {
        System.out.println("Congratulations, you caught the exception!");
        System.out.println("Here is stack trace:");
        e.printStackTrace();
    }
}
}

Example.java

/**
 * Example.java
 * @author Seval
 * @date 10/22/2014
 */
public class Example {
    /**
     * Returns Example2 object
     * If there is no Example2 object, throws exception
     * 
     * @return obj Example2
     * @throws Exception
     */
    public Example2 doExample() throws Exception {
        try {
            // Get the object
            Example2 obj = new Example2();

            return obj;

        } catch (Exception e) {
            // Log the exception and rethrow
            // Log.logException(e);
            throw e;
        }

    }
}

Example2.java

 /**
 * Example2.java
 * @author Seval
 *
 */
public class Example2 {
    /**
     * Constructor of Example2
     * @throws Exception
     */
    public Example2() throws Exception{
        throw new Exception("Please set the \"obj\"");
    }

}

Cela dépend vraiment de savoir si vous espérez trouver l’objet ou non. Si vous suivez l’école de pensée selon laquelle des exceptions devraient être utilisées pour indiquer quelque chose, eh bien, une erreur exceptionnelle s’est produite, puis:

  • objet trouvé; retourne un objet
  • Objet non trouvé; jeter une exception

Sinon, renvoyez null.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top