Em Java (ou qualquer outro idioma com exceções verificadas), ao criar sua própria classe de exceção, como você decide se deve ser verificada ou desmarcada?
Meu instinto é dizer que uma exceção verificada seria solicitada nos casos em que o chamador pudesse se recuperar de alguma maneira produtiva, onde, como uma exceção não verificada, seria mais para casos irrecuperáveis, mas eu estaria interessado nos pensamentos de outras pessoas.
java
exception
checked-exceptions
Matt Sheppard
fonte
fonte
Respostas:
As exceções marcadas são ótimas, desde que você entenda quando elas devem ser usadas. A API principal do Java falha ao seguir essas regras para SQLException (e algumas vezes para IOException), e é por isso que elas são tão terríveis.
As exceções verificadas deve ser usado para previsíveis , mas inevitáveis erros que são razoáveis para se recuperar de .
Exceções não verificadas devem ser usadas para todo o resto.
Vou explicar isso para você, porque a maioria das pessoas não entende o que isso significa.
A menos que a exceção que você está lançando atenda a todas as condições acima, ela deve usar uma exceção não verificada.
Reavaliar em todos os níveis : Às vezes, o método que captura a exceção verificada não é o lugar certo para lidar com o erro. Nesse caso, considere o que é razoável para os seus chamadores. Se a exceção for previsível, imprevisível e razoável para a recuperação, então você deve lançar uma exceção marcada. Caso contrário, você deve agrupar a exceção em uma exceção desmarcada. Se você seguir esta regra, você se converterá exceções verificadas em exceções não verificadas e vice-versa, dependendo da camada em que se encontra.
Para exceções marcadas e desmarcadas, use o nível de abstração correto . Por exemplo, um repositório de código com duas implementações diferentes (banco de dados e sistema de arquivos) deve evitar expor detalhes específicos da implementação, jogando
SQLException
ouIOException
. Em vez disso, ele deve agrupar a exceção em uma abstração que abranja todas as implementações (por exemploRepositoryException
).fonte
De um aluno de Java :
fonte
Há um argumento muito forte para o contrário: nunca use exceções verificadas. Estou relutante em tomar partido no debate, mas parece haver um amplo consenso de que a introdução de exceções verificadas foi uma decisão errada em retrospectiva. Por favor, não atire no mensageiro e consulte esses argumentos .
fonte
foo
estiver documentada como lançadabarException
ao ler o final de um arquivo efoo
chamar um método que lança,barException
mesmo quefoo
não esteja esperando, o código que chamafoo
achará que o final do arquivo foi atingido e não terá idéia de que algo inesperado aconteceu. Eu consideraria essa situação como aquela em que as exceções verificadas devem ser mais úteis, mas também é o único caso em que o compilador permitirá exceções verificadas não tratadas.Em qualquer sistema grande o suficiente, com muitas camadas, as exceções verificadas são inúteis, pois, de qualquer maneira, você precisa de uma estratégia de nível arquitetural para lidar com como a exceção será tratada (use uma barreira de falha)
Com exceções verificadas, sua estratégia de tratamento de erros é microgerenciada e insuportável em qualquer sistema grande.
Na maioria das vezes, você não sabe se um erro é "recuperável" porque não sabe em que camada o chamador da sua API está localizado.
Digamos que eu crie uma API StringToInt que converta a representação de string de um número inteiro em um Int. Devo lançar uma exceção verificada se a API for chamada com a string "foo"? É recuperável? Eu não sei porque na camada dele, o chamador da minha API StringToInt já pode ter validado a entrada, e se essa exceção for lançada, é um bug ou uma corrupção de dados e não é recuperável para essa camada.
Nesse caso, o chamador da API não deseja capturar a exceção. Ele só quer deixar a exceção "borbulhar". Se eu escolher uma exceção marcada, esse chamador terá bastante bloco de captura inútil apenas para repetir artificialmente a exceção.
O que é recuperável depende na maioria das vezes do chamador da API, não do gravador da API. Uma API não deve usar exceções verificadas, pois somente as exceções não verificadas permitem escolher capturar ou ignorar uma exceção.
fonte
Você está certo.
Exceções não verificadas são usadas para deixar o sistema falhar rapidamente, o que é uma coisa boa. Você deve indicar claramente qual é o seu método esperado para funcionar corretamente. Dessa forma, você pode validar a entrada apenas uma vez.
Por exemplo:
Apenas para colocar um exemplo. O ponto é que, se o sistema falhar rapidamente, você saberá onde e por que falhou. Você obterá um rastreamento de pilha como:
E você saberá o que aconteceu. O OtherClass no método "delegateTheWork" (na linha 4569) chamou sua classe com o valor "exit", mesmo quando não deveria etc.
Caso contrário, você teria que espalhar as validações por todo o código e isso é propenso a erros. Além disso, às vezes é difícil rastrear o que deu errado e você pode esperar horas de depuração frustrante
O mesmo acontece com NullPointerExceptions. Se você tiver uma classe de 700 linhas com cerca de 15 métodos, que usa 30 atributos e nenhum deles pode ser nulo, em vez de validar cada um desses métodos para a nulidade, você pode tornar todos esses atributos somente leitura e validá-los no construtor ou método de fábrica.
Exceções verificadas São úteis quando o programador (você ou seus colegas de trabalho) fez tudo certo, validou a entrada, executou testes e todo o código está perfeito, mas o código se conecta a um serviço da web de terceiros que pode estar inoperante (ou um arquivo você estava usando foi excluído por outro processo externo, etc). O serviço da web pode até ser validado antes da tentativa de conexão, mas durante a transferência de dados algo deu errado.
Nesse cenário, não há nada que você ou seus colegas de trabalho possam fazer para ajudá-lo. Mas você ainda precisa fazer alguma coisa e não deixar que o aplicativo morra e desapareça aos olhos do usuário. Você usa uma exceção marcada para isso e lida com a exceção, o que você pode fazer quando isso acontece ?, na maioria das vezes, apenas para tentar registrar o erro, provavelmente salve seu trabalho (o trabalho do aplicativo) e apresente uma mensagem ao usuário . (O site blabla está fora do ar, tente novamente mais tarde etc.)
Se a exceção verificada for usada em excesso (adicionando a "exceção de lançamento" em todas as assinaturas de métodos), seu código se tornará muito frágil, porque todo mundo ignorará essa exceção (porque é muito geral) e a qualidade do código será seriamente comprometida.
Se você usar a exceção desmarcada, algo semelhante acontecerá. Os usuários desse código não sabem se algo pode dar errado; muitas tentativas {...} catch (Throwable t) aparecerão.
fonte
Aqui está minha 'regra final'.
Eu uso:
Compare com a resposta anterior, esta é uma lógica clara (sobre a qual uma pessoa pode concordar ou discordar) para o uso de um ou outro (ou ambos) tipos de exceções.
Para essas duas exceções, criarei minha própria exceção não verificada e verificada para o meu aplicativo (uma boa prática, conforme mencionado aqui ), exceto para uma exceção não verificada muito comum (como NullPointerException)
Por exemplo, o objetivo desta função específica abaixo é criar (ou obter, se já existir) um objeto, o que
significa:
=> exceção desmarcada E limpar o comentário javadoc para essa função chamada)
(escolha do codificador para colocá-lo na CHAMADA: o codificador não verificará o parâmetro nulo, mas o codificador DOCUMENTA)
(responsabilidade e escolha do código do chamado, escolha que será de grande interesse para o chamador
=> exceção verificada, pois todos os chamadores DEVEM tomar uma decisão se o objeto não puder ser criado / encontrado e que A decisão deve ser executada no momento da compilação: eles não podem usar esta função sem precisar lidar com essa possibilidade, ou seja, com essa exceção verificada ).
Exemplo:
fonte
Não se trata apenas da capacidade de recuperar-se da exceção. O que importa mais, na minha opinião, é se o chamador está interessado em capturar a exceção ou não.
Se você escrever uma biblioteca para ser usada em outro lugar ou uma camada de nível inferior em seu aplicativo, pergunte a si mesmo se o chamador está interessado em capturar (conhecer) sua exceção. Se ele não estiver, use uma exceção não verificada, para não sobrecarregá-lo desnecessariamente.
Essa é a filosofia usada por muitos frameworks. Spring e hibernate, em particular, vêm à mente - eles convertem a exceção verificada conhecida em exceção não verificada precisamente porque as exceções verificadas são usadas em excesso no Java. Um exemplo em que consigo pensar é na JSONException do json.org, que é uma exceção verificada e é principalmente irritante - deve ser desmarcada, mas o desenvolvedor simplesmente não pensou nisso.
A propósito, na maioria das vezes o interesse do chamador na exceção está diretamente correlacionado à capacidade de recuperar-se da exceção, mas esse nem sempre é o caso.
fonte
Aqui está uma solução muito simples para o seu dilema Marcado / Não verificado.
Regra 1: Pense em uma exceção não verificada como uma condição testável antes da execução do código. por exemplo…
onde x é nulo ...… o código deve ter o seguinte…
Regra 2: Pense em uma exceção verificada como uma condição não testável que pode ocorrer enquanto o código é executado.
… No exemplo acima, o URL (google.com) pode não estar disponível devido ao servidor DNS estar inativo. Mesmo no instante em que o servidor DNS estava funcionando e resolvia o nome 'google.com' para um endereço IP, se a conexão fosse feita ao google.com, a qualquer momento, a rede poderia cair. Você simplesmente não pode testar a rede o tempo todo antes de ler e gravar em fluxos.
Há momentos em que o código simplesmente deve ser executado antes que possamos saber se há algum problema. Ao forçar os desenvolvedores a escrever seu código de maneira a forçá-los a lidar com essas situações por meio da Checked Exception, tenho que dar gorjeta ao criador de Java que inventou esse conceito.
Em geral, quase todas as APIs em Java seguem as 2 regras acima. Se você tentar gravar em um arquivo, o disco poderá ser preenchido antes de concluir a gravação. É possível que outros processos tenham causado o disco ficar cheio. Simplesmente não há como testar essa situação. Para aqueles que interagem com o hardware onde, a qualquer momento, o uso do hardware pode falhar, as exceções verificadas parecem ser uma solução elegante para esse problema.
Há uma área cinza nisso. No caso de serem necessários muitos testes (uma declaração alucinante se com muitos && e ||), a exceção lançada será uma CheckedException simplesmente porque é muito trabalhoso acertar - você simplesmente não pode dizer esse problema é um erro de programação. Se houver muito menos que 10 testes (por exemplo, 'if (x == null)'), o erro do programador deve ser uma UncheckedException.
As coisas ficam interessantes ao lidar com intérpretes de idiomas. De acordo com as regras acima, um erro de sintaxe deve ser considerado uma exceção verificada ou não verificada? Eu argumentaria que, se a sintaxe do idioma puder ser testada antes de ser executada, deve ser uma UncheckedException. Se o idioma não puder ser testado - semelhante à maneira como o código de montagem é executado em um computador pessoal, o Erro de sintaxe deve ser uma exceção verificada.
As 2 regras acima provavelmente removerão 90% de sua preocupação sobre a escolha. Para resumir as regras, siga este padrão… 1) se o código a ser executado puder ser testado antes de ser executado corretamente e se ocorrer uma exceção - também conhecido como erro de programador, a exceção deve ser uma UncheckedException (uma subclasse de RuntimeException ) 2) se o código a ser executado não puder ser testado antes de ser executado corretamente, a exceção deve ser uma exceção verificada (uma subclasse de exceção).
fonte
Você pode chamá-lo de uma exceção marcada ou desmarcada; no entanto, os dois tipos de exceção podem ser capturados pelo programador, portanto, a melhor resposta é: escreva todas as exceções como desmarcadas e documente-as. Dessa forma, o desenvolvedor que usa sua API pode escolher se deseja capturar essa exceção e fazer alguma coisa. As exceções verificadas são um desperdício completo do tempo de todos e isso torna seu código um pesadelo chocante de se ver. O teste de unidade adequado trará quaisquer exceções que você possa ter que capturar e fazer alguma coisa.
fonte
Exceção verificada: se o cliente puder se recuperar de uma exceção e desejar continuar, use a exceção verificada.
Exceção não verificada: se um cliente não puder fazer nada após a exceção, aumente a exceção não verificada.
Exemplo: Se você espera executar operações aritméticas em um método A () e com base na saída de A (), você deve executar outra operação. Se a saída for nula do método A () que você não espera durante o tempo de execução, é esperado que você ative Exceção de ponteiro nulo, que é exceção de tempo de execução.
Consulte aqui
fonte
Concordo com a preferência por exceções não verificadas como regra, especialmente ao criar uma API. O chamador sempre pode optar por capturar uma exceção documentada e não marcada. Você simplesmente não está forçando desnecessariamente o chamador.
Acho as exceções verificadas úteis no nível inferior, como detalhes da implementação. Muitas vezes, parece um mecanismo de fluxo de controle melhor do que ter que gerenciar um erro "código de retorno" especificado. Às vezes, também pode ajudar a ver o impacto de uma ideia para uma alteração de código de nível baixo ... declarar uma exceção verificada no downstream e ver quem precisaria se ajustar. Este último ponto não se aplica se houver muitos itens genéricos: catch (Exceção e) ou lança Exception, que geralmente não é muito bem pensada.
fonte
Aqui está, quero compartilhar minha opinião que tenho após muitos anos de experiência em desenvolvimento:
Exceção marcada. Isso faz parte do caso de uso de negócios ou do fluxo de chamadas, parte da lógica do aplicativo que esperamos ou não. Por exemplo, conexão rejeitada, a condição não é satisfeita etc. Precisamos lidar com isso e mostrar a mensagem correspondente ao usuário com instruções sobre o que aconteceu e o que fazer em seguida (tente novamente mais tarde, etc.). Eu costumo chamar isso de exceção de pós-processamento ou exceção de "usuário".
Exceção desmarcada. Isso faz parte da exceção de programação, algum erro na programação de código de software (bug, defeito) e reflete uma maneira como os programadores devem usar a API conforme a documentação. Se um documento externo da lib / framework disser que espera obter dados em algum intervalo e não nulos, porque o NPE ou o IllegalArgumentException serão lançados, o programador deve esperar e usar a API corretamente conforme a documentação. Caso contrário, a exceção será lançada. Eu geralmente chamo de exceção de pré-processamento ou exceção de "validação".
Por público-alvo. Agora, vamos falar sobre o público-alvo ou grupo de pessoas em que as exceções foram criadas (conforme minha opinião):
Por fase do ciclo de vida de desenvolvimento de aplicativos.
A razão pela qual as estruturas geralmente usam exceções não verificadas (Spring, por exemplo) é que a estrutura não pode determinar a lógica de negócios do seu aplicativo; cabe aos desenvolvedores capturar e projetar a própria lógica.
fonte
Temos que distinguir esses dois tipos de exceção com base no erro do programador ou não.
FileNotFoundException é um bom exemplo para entender diferenças sutis. FileNotFoundException é lançado caso o arquivo não seja encontrado. Há dois motivos para essa exceção. Se o caminho do arquivo for definido pelo desenvolvedor ou obtendo do usuário final via GUI, deve ser uma exceção não verificada. Se o arquivo for excluído por outra pessoa, deve ser uma exceção verificada.
A exceção verificada pode ser tratada de duas maneiras. Eles estão usando try-catch ou propagam a exceção. No caso de propagação de exceção, todos os métodos na pilha de chamadas serão fortemente acoplados devido ao tratamento de exceções. É por isso que temos que usar a exceção verificada com cuidado.
Caso você desenvolva um sistema corporativo em camadas, é necessário escolher a exceção não verificada a ser lançada, mas não se esqueça de usar a exceção verificada para o caso de não poder fazer nada.
fonte
As exceções marcadas são úteis para casos recuperáveis em que você deseja fornecer informações ao chamador (por exemplo, permissões insuficientes, arquivo não encontrado, etc.).
Exceções não verificadas são usadas raramente, se houver, para informar o usuário ou programador sobre erros graves ou condições inesperadas durante o tempo de execução. Não jogue-os se estiver escrevendo código ou bibliotecas que serão usadas por outros, pois eles podem não estar esperando que seu software gere exceções não verificadas, pois o compilador não os força a serem capturados ou declarados.
fonte
Sempre que uma exceção é menos provável, e podemos prosseguir mesmo depois de capturá-la, e não podemos fazer nada para evitar essa exceção, então podemos usar a exceção verificada.
Sempre que queremos fazer algo significativo quando uma exceção específica acontece e quando essa exceção é esperada, mas não certa, podemos usar a exceção verificada.
Sempre que a exceção navega em camadas diferentes, não precisamos capturá-la em todas as camadas; nesse caso, podemos usar a exceção de tempo de execução ou agrupar a exceção como exceção não verificada.
A exceção de tempo de execução é usada quando a exceção é mais provável de ocorrer, não há como ir além e nada pode ser recuperável. Portanto, neste caso, podemos tomar precauções com relação a essa exceção. EX: NUllPointerException, ArrayOutofBoundsException. É mais provável que isso aconteça. Nesse cenário, podemos tomar precauções durante a codificação para evitar essa exceção. Caso contrário, teremos que escrever blocos try catch em todos os lugares.
Exceções mais gerais podem ser feitas Desmarcadas, menos gerais são verificadas.
fonte
Penso que podemos pensar nas exceções de várias perguntas:
por que a exceção acontece? O que podemos fazer quando isso acontece
por engano, um bug. como um método de objeto nulo é chamado.
Esse tipo de exceção deve ser corrigido durante o teste. Caso contrário, ele interrompe a produção e você tem um bug muito alto que precisa ser corrigido imediatamente. Esse tipo de exceção não precisa ser verificado.
por entrada de externo, você não pode controlar ou confiar na saída do serviço externo.
Aqui, pode ser necessário verificar se o nome é nulo se você quiser continuar quando for nulo; caso contrário, poderá deixá-lo em paz e ele será interrompido aqui e dará ao chamador a exceção do tempo de execução. Esse tipo de exceção não precisa ser verificado.
por exceção de tempo de execução de externo, você não pode controlar ou confiar no serviço externo.
Aqui, pode ser necessário capturar todas as exceções do ExternalService se você quiser continuar quando isso acontecer, caso contrário, poderá deixar em paz e ele será interrompido aqui e dará ao chamador a exceção do tempo de execução.
por exceção verificada de externo, você não pode controlar ou confiar no serviço externo.
Aqui, pode ser necessário capturar todas as exceções do ExternalService se você quiser continuar quando isso acontecer, caso contrário, poderá deixar em paz e ele será interrompido aqui e dará ao chamador a exceção do tempo de execução.
Nesse caso, precisamos saber que tipo de exceção aconteceu no ExternalService? Depende:
se você pode lidar com alguns tipos de exceções, precisa capturá-las e processá-las. Para outros, borbulhe-os.
se você precisar de log ou resposta para executar a execução específica, poderá capturá-los. Para outros, borbulhe-os.
fonte
Eu acho que, ao declarar uma exceção de aplicativo, deve ser uma exceção não verificada, ou seja, subclasse de RuntimeException. O motivo é que ele não irá desorganizar o código do aplicativo com try-catch e lança a declaração no método. Se seu aplicativo estiver usando o Java Api, que lança exceções verificadas que de qualquer maneira precisam ser manipuladas. Para outros casos, o aplicativo pode lançar uma exceção desmarcada. Se o chamador do aplicativo ainda precisar lidar com a exceção não verificada, isso poderá ser feito.
fonte
A regra que eu uso é: nunca use exceções não verificadas! (ou quando você não vê nenhuma maneira de contornar isso)
Do ponto de vista do desenvolvedor que usa sua biblioteca ou do usuário final que usa sua biblioteca / aplicativo, é realmente péssimo ser confrontado com um aplicativo que trava devido a uma exceção não solicitada. E contar com um catch-all também não é bom.
Dessa forma, o usuário final ainda pode receber uma mensagem de erro, em vez de o aplicativo desaparecer completamente.
fonte