Lançando e capturando exceções na mesma função / método

10

Eu escrevi uma função que solicita a entrada de um usuário até que ele insira um número inteiro positivo (um número natural). Alguém disse que eu não deveria lançar e capturar exceções em minha função e deixar que o chamador de minha função lide com elas.

Eu me pergunto o que outros desenvolvedores pensam sobre isso. Também provavelmente estou usando mal exceções na função. Aqui está o código em 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;
}

Estou interessado em duas coisas:

  1. Devo deixar o chamador se preocupar com exceções? O ponto da função é que incomoda o usuário até que ele insira um número natural. O ponto da função é ruim? Não estou falando de interface do usuário (o usuário não consegue sair do loop sem a entrada adequada), mas de entrada em loop com exceções tratadas.
  2. Você diria que a instrução throw (neste caso) é um uso indevido de exceções? Eu poderia criar facilmente um sinalizador para verificar a validade do número e enviar a mensagem de aviso com base nesse sinalizador. Mas isso adicionaria mais linhas ao código e acho que é perfeitamente legível.

O problema é que muitas vezes escrevo uma função de entrada separada. Se o usuário precisar inserir um número várias vezes, eu crio uma função separada para entrada que lida com todas as exceções e limitações de formatação.

usr
fonte
Depende muito do idioma. Alguns idiomas usam exceções mais livremente que outros.
Martin York

Respostas:

11

O ponto de uma exceção é que ele permite que um método informe ao chamador que entrou em um estado em que não poderia continuar normalmente, sem forçar a incorporação de um código de erro no valor de retorno.

No seu caso, seu método sabe exatamente o que fazer quando a entrada não for maior que 0. O único motivo para salvar linhas aqui é porque você está lançando a mesma exceção que receberia se a entrada não fosse um número. No entanto, a exceção que você está lançando não representa adequadamente por que seu código não gosta da entrada. Se alguém aparecesse para ver esse código, teria que gastar um tempo extra tentando ver exatamente como as coisas estão funcionando.

unholysampler
fonte
Sim, é por isso que eu pensei que era um mau uso. Então, ignorando essa instrução throw, você concorda que não há problema em lidar com exceções nesse loop dentro de uma função em vez de permitir que o chamador as capture? A instrução catch está lá por causa de Integer.parseInt (novamente, ignorando o lançamento).
usr
11
@usr: Essa resposta depende muito mais de como o resto do sistema deve funcionar juntos. Exceções precisam ser tratadas em algum momento. Existem algumas maneiras diferentes de organizá-lo e essa é uma delas. O que é melhor depende de outras informações que não temos aqui.
Unholysampler
4

Este é um mau uso de exceções. Para começar, um número não positivo não é uma exceção de formato.

Por que usar exceções? Se você souber que entrada não é permitida, apenas não saia do loop até obter entrada válida do usuário, algo como o seguinte:

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
fonte
Integer.intParse lança exceções de formato, portanto, o uso da instrução catch é perfeitamente válido. Quero principalmente saber se não há problema em usar a instrução catch em um loop em uma função ou devo deixar o chamador da função manipular a exceção de formato.
usr
Integer.parseInt()lança a NumberFormatExceptionse não puder analisar o argumento de string fornecido. Não há necessidade de você (e você não será capaz) de lançar a exceção você mesmo.
22711 Bernard
Eu editei o exemplo de código para ser mais explícito.
22711 Bernard
"e você não será capaz) de lançar a exceção você mesmo" - o que você quer dizer com isso?
precisa
@ Michael Borgwardt: quero dizer que, como o Integer.parseInt()método lançará a exceção para você, se ocorrer, você não poderá lançá-lo você mesmo depois, pois ele já foi lançado.
22711 Bernard Bernard
1

Capture exceções apenas se você pretende fazer algo relevante para a chamada de método atual; ou seja, limpeza, lógica de falha etc. Nesse caso, a captura está apenas enviando uma mensagem ao console, não é relevante para o método sideInput, portanto, pode ser manuseada ainda mais na cadeia / pilha de chamadas.

Pode-se se livrar da tentativa / captura aqui e simplesmente documentar a chamada do método:

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

Ainda é preciso lidar com essa exceção na cadeia / pilha de chamadas!

Jon Raynor
fonte
11
A mensagem está lá apenas para uma melhor interface do usuário. O objetivo da função é que incomoda o usuário para entrada até que ele / ela insira uma entrada válida. Isso não pode ser feito sem os blocos try-catch. Minha pergunta era se o argumento em si é válido. Acho que sim, mas alguém me disse que eu deveria remover os blocos try-catch e deixar o chamador lidar com essa exceção específica. Mas então a função não funcionará como pretendido.
usr
1

Você não deve lançar e capturar a mesma exceção em um método, acho que o bloco catch capturará a mesma exceção que você está lançando, portanto você não está realmente lançando.

Se parseIntfoi bem sucedido, então não é um NumberFormatException.

se o lado for menor que zero, você deve lançar a NegativeSideLengthException;

Crie uma exceção de negócios / personalizada chamada NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


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

}

Em seguida, sideInputlança 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;
}

Você pode até (se quiser) adicionar outro bloco catch para pegar NegativeSideLengthExceptione não ter o método para lançá-lo.

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

Sinalizadores não são uma boa maneira de lidar com exceções.

Tulains Córdova
fonte
-1

Exceções são coisas bastante pesadelos, elas trazem mais complexidades do que resolvem.

Em primeiro lugar, se você não capturar suas exceções, o chamador só poderá fazer on error resume next, ou seja, depois de uma semana, você não saberá o que sua função pode lançar e o que fazer com ela:

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

Basicamente, se você os obtiver, precisará ter um entendimento muito bom sobre contratos e garantias de exceção. Isso raramente acontece no mundo real. E também será difícil ler seu código.

Além disso, o mais engraçado é que, se você realmente tem alguma chance de lidar com exceções, precisa da linguagem RAII real, o que é bem humorado, já que Java e .NET são sobre exceções ...

Repetindo isso mais uma vez, mas ...:

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

Codificador
fonte
4
-1 para postar um discurso borderline, cheio de instruções não muito corretas - ou pelo menos dependentes do contexto / idioma -, em vez de responder à pergunta real do OP.
Péter Török
@ PéterTörök: "Dê a um homem um peixe e você o alimenta por um dia. Ensine um homem a pescar e você o alimenta por toda a vida." Existem problemas muito sérios por trás das exceções, e as pessoas devem conhecê-las -1000 / + 1, eu realmente não me importo.
Coder
11
Sinta-se livre para acreditar que você pode escrever o código correto com mais facilidade, sem exceções. Apenas não exponha suas opiniões e crenças como fatos (mesmo que Joel seja da mesma opinião, ainda é uma opinião, não um fato), e não publique respostas irrelevantes no SE.
Péter Török
@ PéterTörök: Não é uma opinião, é um fato, toda vez que você usa um componente que gera uma exceção internamente, é necessário rastrear toda a hierarquia e verificar todas as linhas de código para saber o que pegar e se os componentes oferecem forte garantia, e tudo é revertido, ou se esse problema for apenas uma falsa sensação de segurança. Caramba, você nem conhece todas as exceções que o std :: string lança, você pode procurar as especificações, mas nunca encontrará. Coisas como : throw(thisexception, thatexception)essas são absolutamente erradas e nunca devem ser usadas, pois, caso contrário, você será inesperado, exceto.
Coder
2
OK, e o código que não usa exceções? Puxa, você precisa rastrear o código para verificar cada linha para ver se os valores de retorno são tratados adequadamente ou ignorados. E quando um valor de retorno é ignorado, ninguém percebe até que talvez seu aplicativo trave algumas milhares de linhas depois. Exceções, pelo menos, forçam você a prestar atenção. Sim, você pode engoli-los - mas apenas com um explícito catch. Embora um valor de retorno ignorado não seja pesquisável - ele é identificável apenas através da revisão de código linha por linha. Por último, mas não menos importante, os construtores não têm valores de retorno, esse é o principal motivo para usar exceções.
Péter Török