Lancer et intercepter des exceptions dans la même fonction / méthode

10

J'ai écrit une fonction qui demande à un utilisateur de saisir jusqu'à ce que l'utilisateur entre un entier positif (un nombre naturel). Quelqu'un a dit que je ne devrais pas lancer et intercepter des exceptions dans ma fonction et laisser l'appelant de ma fonction les gérer.

Je me demande ce que les autres développeurs en pensent. J'utilise également probablement des exceptions dans la fonction. Voici le code en Java:

private static int sideInput()
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                // probably a misuse of exceptions
                throw new NumberFormatException();
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

Je m'intéresse à deux choses:

  1. Dois-je laisser l'appelant s'inquiéter des exceptions? Le point de la fonction est qu'elle harcèle l'utilisateur jusqu'à ce que l'utilisateur entre un nombre naturel. Le point de la fonction est-il mauvais? Je ne parle pas de l'interface utilisateur (l'utilisateur ne pouvant pas sortir de la boucle sans entrée appropriée), mais de l'entrée en boucle avec des exceptions gérées.
  2. Diriez-vous que l'instruction throw (dans ce cas) est une mauvaise utilisation des exceptions? Je pourrais facilement créer un indicateur pour vérifier la validité du numéro et afficher le message d'avertissement basé sur cet indicateur. Mais cela ajouterait plus de lignes au code et je pense qu'il est parfaitement lisible tel quel.

Le fait est que j'écris souvent une fonction d'entrée séparée. Si l'utilisateur doit saisir un nombre plusieurs fois, je crée une fonction distincte pour l'entrée qui gère toutes les exceptions et limitations de mise en forme.

usr
la source
Dépend fortement de la langue. Certaines langues utilisent les exceptions plus librement que d'autres.
Martin York

Réponses:

11

Le point d'une exception est qu'elle permet à une méthode de dire à l'appelant qui est entré dans un état où il ne pouvait pas continuer normalement, sans vous forcer à incorporer un code d'erreur dans la valeur de retour.

Dans votre cas, votre méthode sait exactement quoi faire lorsque l'entrée n'est pas supérieure à 0. La seule raison pour laquelle vous enregistrez des lignes ici est que vous lancez la même exception que vous obtiendriez si l'entrée n'était pas un nombre. Cependant, l'exception que vous lancez ne représente pas correctement pourquoi votre code n'aime pas l'entrée. Si quelqu'un d'autre venait et voyait ce code, il devrait passer plus de temps à essayer de voir exactement comment les choses fonctionnent.

unholysampler
la source
Ouais, c'est pourquoi je pensais que c'était une mauvaise utilisation. Donc, en ignorant cette instruction throw, vous convenez qu'il est OK de gérer les exceptions dans une telle boucle à l'intérieur d'une fonction au lieu de laisser l'appelant les attraper? L'instruction catch est là à cause d'Integer.parseInt (encore une fois, en ignorant le lancer).
usr
1
@usr: Cette réponse dépend beaucoup plus de la façon dont le reste du système est censé fonctionner ensemble. Les exceptions doivent être gérées à un moment donné. Il y a plusieurs façons de l'organiser et c'est l'une d'entre elles. Ce qui dépend le mieux des autres informations que nous n'avons pas ici.
unholysampler
4

C'est une mauvaise utilisation des exceptions. Pour commencer, un nombre non positif n'est pas une exception de format.

Pourquoi utiliser des exceptions? Si vous savez quelle entrée n'est pas autorisée, ne sortez pas de la boucle avant d'avoir obtenu une entrée valide de l'utilisateur, quelque chose comme ceci:

while (true)
{
   // Get user input.
   String input = scanner.nextLine();

   try
   {
      side = Integer.parseInt(input);

      break;
   }
   catch (NumberFormatException ex)
   {
      // Inform user of invalid input.
      System.out.println("Invalid input. Enter a natural number.");
   }
}
Bernard
la source
Integer.intParse lève une exception de format, donc l'utilisation de l'instruction catch est parfaitement valide. Je veux principalement savoir s'il est OK d'utiliser l'instruction catch dans une boucle dans une fonction ou dois-je laisser l'appelant de la fonction gérer l'exception de format.
usr
Integer.parseInt()jette un NumberFormatExceptions'il ne peut pas analyser l'argument de chaîne fourni. Vous n'avez pas besoin (et vous ne pourrez pas) de lever l'exception vous-même.
Bernard
J'ai édité l'exemple de code pour être plus explicite.
Bernard
"et vous ne pourrez pas) lever l'exception vous-même" - que voulez-vous dire par là?
Michael Borgwardt
@Michael Borgwardt: Je veux dire que puisque la Integer.parseInt()méthode lèvera l'exception pour vous si elle se produit, vous ne pourrez pas la lancer vous-même ensuite car elle a déjà été lancée.
Bernard
1

Ne capturez les exceptions que si vous avez l'intention de faire quelque chose qui est pertinent pour l'appel de méthode en cours; c.-à-d. nettoyage, logique d'échec, etc. Dans ce cas, le catch envoie simplement un message à la console, il n'est pas pertinent pour la méthode sideInput, il peut donc être traité plus haut dans la chaîne / pile d'appels.

On peut se débarrasser du try / catch ici et simplement documenter l'appel de méthode:

//Throws NumberFormatException if read input is less than 0
private static int sideInput()

Il faut encore gérer cette exception plus haut dans la chaîne / pile d'appels!

Jon Raynor
la source
1
Le message est là juste pour une meilleure interface utilisateur. Le point de la fonction est qu'elle harcèle l'utilisateur pour entrée jusqu'à ce qu'il / elle entre une entrée valide. Cela ne peut pas être fait sans blocs try-catch. Ma question était de savoir si le point lui-même est valable. Je pense que oui, mais quelqu'un m'a dit que je devrais supprimer les blocs try-catch et laisser l'appelant gérer cette exception particulière. Mais alors la fonction ne fonctionnera pas comme prévu.
usr
1

Vous ne devriez pas à la fois lancer et intercepter la même exception dans une méthode, je pense même que le bloc catch interceptera la même exception que vous lancez, donc vous ne la lancez pas vraiment.

Si cela parseInta réussi, ce n'est pas le cas NumberFormatException.

si le côté est inférieur à zéro, vous devez lancer un NegativeSideLengthException;

Créer une exception personnalisée / commerciale nommée NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


    public NegativeSideLengthException(Integer i)
    {
        super("Invalid negative side length "+i);        
    }

}

sideInputLance ensuite NegativeSideLengthException

private static int sideInput() throws NegativeSideLengthException
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                throw new NegativeSideLengthException(side);
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

Vous pouvez même (si vous le souhaitez) ajouter un autre bloc catch à catch NegativeSideLengthExceptionet ne pas demander à la méthode de le lancer.

do {
    System.out.print("Side length: ");
    input = scanner.nextLine();
    try {
        side = Integer.parseInt(input);
        if (side <= 0) {
            throw new NegativeSideLengthException(side);
        }
    }
    catch (NumberFormatException numFormExc) {
        System.out.println("Invalid input. Enter a natural number.");
    } catch (NegativeSideLengthException e){
        System.out.println("Invalid input. Enter a non-negative number.");
    }
} while (side <= 0);

Les indicateurs ne sont pas un bon moyen de gérer les exceptions.

Tulains Córdova
la source
-1

Les exceptions sont des choses assez cauchemardesques, elles apportent plus de complexité qu'elles n'en résolvent.

Premièrement, si vous n'attrapez pas vos exceptions, l'appelant ne peut que le faire on error resume next, c'est-à-dire qu'au bout d'une semaine, même vous ne saurez pas ce que votre fonction peut lancer, et quoi en faire:

{
    ...
}
catch(OutOfMemory, CorruptedMemory, BadData, DanglingPointers, UnfinishedCommit)
{
    Console.WriteLine("Nothing to see here, move on.");
    Console.WriteLine("The app is very stable, see, no crashing!");
}

Fondamentalement, si vous les attrapez, vous devez avoir une très bonne compréhension des contrats et des garanties d'exception. Cela se produit rarement dans un monde réel. Et aussi votre code sera difficile à lire.

De plus, ce qui est drôle, c'est que si vous avez vraiment une chance de gérer les exceptions, vous avez besoin d'un vrai langage RAII, qui est en quelque sorte humoristique, car Java et .NET sont tout au sujet des exceptions ...

Répéter encore une fois, mais ...:

http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx

http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx

http://www.joelonsoftware.com/items/2003/10/13.html

Codeur
la source
4
-1 pour poster une diatribe limite, pleine de déclarations pas tout à fait correctes - ou du moins dépendant du contexte / de la langue -, au lieu de répondre à la question réelle du PO.
Péter Török
@ PéterTörök: "Donnez un poisson à un homme et vous le nourrissez pendant une journée. Apprenez à un homme à pêcher et vous le nourrissez toute sa vie." Il y a de très graves problèmes derrière les exceptions, et les gens devraient les connaître -1000 / + 1, je m'en fous vraiment.
Coder
1
N'hésitez pas à croire que vous pouvez écrire le code correct plus facilement sans exception. Ne déclarez pas vos opinions et vos convictions comme des faits (même si Joel est du même avis, c'est toujours une opinion, pas un fait), et ne postez pas de réponses non pertinentes sur SE.
Péter Török
@ PéterTörök: Ce n'est pas une opinion, c'est un fait, chaque fois que vous utilisez un composant qui lève une exception en interne, vous devez explorer toute la hiérarchie et vérifier chaque ligne de code pour savoir quoi attraper, et si les composants offrent une forte garantie, et tout est annulé, ou si cette prise est juste un faux sentiment de sécurité. Heck, vous ne connaissez même pas toutes les exceptions lancées par std :: string, vous pouvez consulter les spécifications, mais vous ne les trouverez jamais. Des choses comme : throw(thisexception, thatexception)sont carrément erronées et ne devraient jamais être utilisées, car sinon vous obtiendrez inattendu sauf.
Coder
2
OK, alors qu'en est-il du code qui n'utilise pas d'exceptions? Gee, vous devez parcourir le code pour vérifier chaque ligne pour voir si les valeurs de retour sont gérées correctement ou ignorées. Et lorsqu'une valeur de retour est ignorée, personne ne le remarque jusqu'à ce que votre application se bloque quelques milliers de lignes plus tard. Les exceptions vous obligent au moins à en tenir compte. Oui, vous pouvez les avaler - mais uniquement avec une explicite catch. Bien qu'une valeur de retour ignorée ne soit pas consultable - elle n'est identifiable que via une révision de code ligne par ligne. Enfin et surtout, les constructeurs n'ont pas de valeur de retour, c'est la principale raison d'utiliser des exceptions.
Péter Török