Generare e catturare eccezioni nella stessa funzione / metodo


10

Ho scritto una funzione che richiede all'utente un input fino a quando l'utente non inserisce un numero intero positivo (un numero naturale). Qualcuno ha detto che non avrei dovuto gettare e catturare eccezioni nella mia funzione e che avrei dovuto gestirle il chiamante della mia funzione.

Mi chiedo cosa ne pensano gli altri sviluppatori. Probabilmente sto anche abusando delle eccezioni nella funzione. Ecco il codice in 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;
}

Sono interessato a due cose:

  1. Devo lasciare che il chiamante si preoccupi delle eccezioni? Il punto della funzione è che assilla l'utente finché l'utente non inserisce un numero naturale. Il punto della funzione è negativo? Non sto parlando dell'interfaccia utente (l'utente non è in grado di uscire dal ciclo senza l'input corretto), ma dell'input in loop con le eccezioni gestite.
  2. Diresti che la dichiarazione di lancio (in questo caso) è un abuso di eccezioni? Potrei facilmente creare un flag per verificare la validità del numero e generare il messaggio di avviso basato su quel flag. Ma ciò aggiungerebbe più righe al codice e penso che sia perfettamente leggibile così com'è.

Il fatto è che scrivo spesso una funzione di input separata. Se l'utente deve inserire un numero più volte, creo una funzione separata per l'input che gestisce tutte le eccezioni e limitazioni di formattazione.


Dipende fortemente dalla lingua. Alcune lingue usano le eccezioni più liberamente di altre.
Martin York,

Risposte:


11

Il punto di un'eccezione è che consente a un metodo di dire a quel chiamante che è entrato in uno stato in cui non potrebbe continuare normalmente, senza costringere l'utente a incorporare un codice di errore nel valore restituito.

Nel tuo caso, il tuo metodo sa esattamente cosa fare quando l'input non è maggiore di 0. L'unico motivo per cui stai salvando le righe qui è perché ti capita di lanciare la stessa eccezione che otterresti se l'input non fosse un numero. Tuttavia, l'eccezione che stai generando non rappresenta correttamente il motivo per cui al tuo codice non piace l'input. Se qualcun altro dovesse venire a vedere questo codice, dovrebbe passare del tempo extra cercando di vedere esattamente come funzionano le cose.


Sì, è per questo che ho pensato che fosse un uso improprio. Quindi, ignorando quella frase di lancio, accetti che sia OK gestire le eccezioni in tale ciclo all'interno di una funzione invece di lasciare che il chiamante le prenda? L'istruzione catch è lì a causa di Integer.parseInt (di nuovo, ignorando il lancio).
usr

1
@usr: questa risposta dipende molto di più da come il resto del sistema dovrebbe funzionare insieme. Le eccezioni devono essere gestite ad un certo punto. Esistono diversi modi per organizzarlo e questo è uno di questi. Qual è il migliore dipende da altre informazioni che non abbiamo qui.
unholysampler il

4

Questo è un cattivo uso delle eccezioni. Per cominciare, un numero non positivo non è un'eccezione di formato.

Perché usare le eccezioni? Se sai quale input non è consentito, non uscire dal ciclo fino a quando non ottieni un input valido dall'utente, simile al seguente:

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.");
   }
}

Integer.intParse genera eccezioni di formato, quindi l'utilizzo dell'istruzione catch è perfettamente valido. Voglio principalmente sapere se è corretto utilizzare l'istruzione catch in un ciclo in una funzione o devo lasciare che il chiamante della funzione gestisca l'eccezione del formato.
usr

Integer.parseInt()genera un NumberFormatExceptionse non riesce ad analizzare l'argomento stringa fornito. Non è necessario per te (e non sarai in grado) di lanciare tu stesso l'eccezione.
Bernard,

Ho modificato l'esempio di codice per essere più esplicito.
Bernard,

"e non sarai in grado di) lanciare tu stesso l'eccezione" - cosa intendi dire lì?
Michael Borgwardt,

@Michael Borgwardt: Voglio dire che dal momento che il Integer.parseInt()metodo getterà l'eccezione per te se si verifica, non sarai in grado di lanciarlo da solo in quanto è già stato lanciato.
Bernard,

1

Cattura le eccezioni solo se intendi fare qualcosa che è rilevante per la chiamata del metodo corrente; cioè pulizia, logica di errore, ecc. In questo caso, il fermo sta semplicemente inviando un messaggio alla console, non è rilevante per il metodo sideInput, quindi può essere gestito più in alto nella catena / stack di chiamate.

Si può sbarazzarsi del tentativo / cattura qui e semplicemente documentare la chiamata del metodo:

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

Bisogna ancora gestire quell'eccezione più in alto nella catena / stack di chiamate!


1
Il messaggio è lì solo per una migliore interfaccia utente. Il punto della funzione è che assilla l'utente per l'input fino a quando non inserisce un input valido. Questo non può essere fatto senza blocchi try-catch. La mia domanda era se il punto stesso fosse valido. Penso di si, ma qualcuno mi ha detto che dovrei rimuovere i blocchi try-catch e lasciare che il chiamante gestisca questa particolare eccezione. Ma poi la funzione non funzionerà come previsto.
usr

1

Non dovresti lanciare e catturare la stessa eccezione in un metodo, penso anche che il blocco catch catturi la stessa eccezione che stai lanciando, quindi non lo stai davvero lanciando.

Se ha parseIntavuto successo, allora non è un NumberFormatException.

se side è inferiore a zero, dovresti lanciare a NegativeSideLengthException;

Crea un'eccezione personalizzata / commerciale denominata NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


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

}

Quindi sideInputgenera 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;
}

Puoi anche (se vuoi) aggiungere un altro blocco catch per catturare NegativeSideLengthExceptione non avere il metodo lanciarlo.

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);

Le bandiere non sono un buon modo per gestire le eccezioni.


-1

Le eccezioni sono cose piuttosto da incubo, portano più complessità di quelle che risolvono.

In primo luogo, se non cogli le tue eccezioni, il chiamante può fare solo on error resume next, cioè dopo una settimana, anche tu non saprai cosa può lanciare la tua funzione e cosa farne:

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

Fondamentalmente, se li prendi, devi avere un'ottima conoscenza dei contratti e garanzie di eccezione. Ciò accade raramente in un mondo reale. E anche il tuo codice sarà difficile da leggere.

Inoltre, la cosa divertente è che se hai davvero la possibilità di gestire le eccezioni hai bisogno del vero linguaggio RAII, che è una specie di umorismo, dal momento che Java e .NET si basano su eccezioni ...

Ripetendolo ancora una volta, ma ...:

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


4
-1 per la pubblicazione di un rant borderline, pieno di affermazioni non del tutto corrette - o almeno dipendenti dal contesto / dalla lingua, invece di rispondere alla domanda effettiva del PO.
Péter Török,

@ PéterTörök: "Dai un pesce a un uomo e gli dai da mangiare per un giorno. Insegna a un uomo a pescare e gli dai da mangiare per tutta la vita." Ci sono problemi molto seri dietro le eccezioni e le persone dovrebbero conoscerle -1000 / + 1, non mi interessa davvero.
Coder

1
Sentiti libero di credere che puoi scrivere il codice corretto più facilmente senza eccezioni. Basta non dichiarare le tue opinioni e credenze come fatti (anche se Joel è della stessa opinione, è ancora un'opinione, non un fatto), e non pubblicare risposte irrilevanti su SE.
Péter Török,

@ PéterTörök: non è un'opinione, è un dato di fatto, ogni volta che si utilizza un componente che genera un'eccezione internamente, è necessario eseguire la ricerca per indicizzazione dell'intera gerarchia e controllare ogni singola riga di codice per sapere cosa catturare e se i componenti offrono solidi garanzia, e tutto viene ripristinato, o se quella cattura è solo un falso senso di sicurezza. Cavolo, non conosci nemmeno tutte le eccezioni lanciate da std :: string, puoi cercare le specifiche, ma non troverai mai. Cose come : throw(thisexception, thatexception)sono assolutamente sbagliate e non dovrebbero mai essere usate, perché altrimenti otterrai imprevisti tranne.
Coder

2
OK, quindi che ne dici del codice che non utilizza le eccezioni? Accidenti, devi scorrere il codice per controllare ogni singola riga per vedere se i valori di ritorno sono gestiti correttamente o ignorati. E quando un valore di ritorno viene ignorato, nessuno nota fino a quando forse l'app non si arresta in modo anomalo di un paio di migliaia di righe in seguito. Le eccezioni ti obbligano almeno a prenderne atto. Sì, puoi ingoiarli, ma solo con un esplicito catch. Mentre un valore restituito ignorato non è ricercabile, è identificabile solo tramite revisione del codice riga per riga. Ultimo ma non meno importante, i costruttori non hanno valori di ritorno, questa è la ragione principale per usare le eccezioni.
Péter Török,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.