Em Java, para que servem as exceções verificadas? [fechadas]


14

As exceções verificadas de Java foram prejudicadas ao longo dos anos. Um sinal revelador é que é literalmente o único idioma do mundo que os possui (nem mesmo outros idiomas da JVM como Groovy e Scala). Bibliotecas Java proeminentes como Spring e Hibernate também não as usam.

Eu, pessoalmente, encontrei um uso para eles ( na lógica de negócios entre as camadas), mas, caso contrário, sou uma exceção bastante verificada.

Existem outros usos que eu não percebo?


Todas as bibliotecas principais do Java usam as exceções verificadas bastante extensivamente.
Joonas Pulakka

3
@ Joonas eu sei, e é uma dor!
Brad Cupit

Respostas:


22

Primeiro de tudo, como qualquer outro paradigma de programação, você precisa fazer o certo para que funcione bem.

Para mim, a vantagem das exceções verificadas é que os autores da biblioteca de tempo de execução Java JÁ decidiram por mim que problemas comuns eu poderia razoavelmente esperar que fosse capaz de lidar no ponto de chamada (em oposição a um catch-print- de nível superior bloco de matriz) e considere o mais cedo possível como lidar com esses problemas.

Eu gosto de exceções verificadas porque elas tornam meu código mais robusto, forçando-me a pensar na recuperação de erros o mais cedo possível.

Para ser mais preciso, para mim isso torna meu código mais robusto, pois me obriga a considerar casos de canto estranhos muito cedo no processo, em vez de dizer "Opa, meu código não funciona se o arquivo ainda não existir" com base em um erro na produção, que você precisará refazer seu código para manipular. Adicionar manipulação de erro ao código existente pode ser uma tarefa não trivial - e, portanto, cara - quando se chega à manutenção, em vez de apenas fazer isso desde o início.

Pode ser que o arquivo ausente seja algo fatal e deva causar uma falha no programa, mas você toma essa decisão com

} catch (FileNotFoundException e) {
  throw new RuntimeException("Important file not present", e);
}

Isso também mostra um efeito colateral muito importante. Se você quebrar uma exceção, poderá adicionar uma explicação que acompanha o rastreamento de pilha ! Isso é extremamente poderoso porque você pode adicionar informações sobre, por exemplo, o nome do arquivo que estava faltando, ou os parâmetros passados ​​para esse método ou outras informações de diagnóstico, e essas informações estão presentes diretamente no rastreamento da pilha, que frequentemente é a única coisa que você começa quando um programa falha.

As pessoas podem dizer "podemos apenas executar isso no depurador para reproduzir", mas descobri que com muita freqüência os erros de produção não podem ser reproduzidos posteriormente, e não podemos executar depuradores na produção, exceto em casos muito desagradáveis ​​em que essencialmente seu trabalho está em jogo.

Quanto mais informações em seu rastreamento de pilha, melhor. As exceções verificadas me ajudam a obter essas informações rapidamente.


EDIT: Isso vale para designers de bibliotecas também. Uma biblioteca que eu uso diariamente contém muitas exceções verificadas que poderiam ter sido projetadas muito melhor, tornando-a menos tediosa de usar.


+1. Os designers da Spring fizeram um bom trabalho ao traduzir muitas exceções do Hibernate em exceções não verificadas.
Fil

FYI: Frank Sommers mencionou neste tópico que o software tolerante a falhas geralmente depende do mesmo mecanismo. Talvez você possa subdividir sua resposta no caso recuperável e no caso fatal.
Rwong 14/05

@rwong, você poderia explicar com mais detalhes o que tem em mente?

11

Você tem duas boas respostas que explicam quais exceções verificadas se tornaram na prática. (+1 para ambos.) Mas também valeria a pena examinar o que eles pretendiam em teoria, porque a intenção realmente vale a pena.

As exceções verificadas são realmente destinadas a tornar o idioma mais seguro. Considere um método simples como multiplicação de números inteiros. Você pode pensar que o tipo de resultado desse método seria um número inteiro, mas, estritamente falando, o resultado é uma exceção de número inteiro ou excedente. Considerando o resultado inteiro por si só como o tipo de retorno do método, não expressa todo o intervalo da função.

Visto sob esse prisma, não é estritamente verdade dizer que as exceções verificadas não foram encontradas em outros idiomas. Eles simplesmente não assumiram a forma que o Java usou. Nos aplicativos Haskell, é comum usar tipos de dados algébricos para distinguir entre a conclusão bem-sucedida e malsucedida da função. Embora isso não seja uma exceção, por si só, a intenção é praticamente a mesma que uma exceção verificada; é uma API projetada para forçar o consumidor da API a lidar com os bem-sucedidos no caso malsucedido, por exemplo:

data Foo a =
     Success a
   | DidNotWorkBecauseOfA
   | DidNotWorkBecauseOfB

Isso informa ao programador que a função tem dois resultados possíveis adicionais além do sucesso.


+1 para exceções verificadas sobre segurança de tipo. Eu nunca ouvi essa ideia antes, mas faz muito sentido.
Ixrec 15/02

11

Na OMI, as exceções verificadas são uma implementação falha do que pode ter sido uma idéia bastante decente (em circunstâncias completamente diferentes - mas na verdade nem sequer é uma boa idéia nas circunstâncias atuais).

A boa idéia é que seria bom que o compilador verifique se todas as exceções que um programa tem potencial para lançar serão capturadas. A implementação falha é que, para fazer isso, o Java obriga a lidar com a exceção diretamente em qualquer classe que invoque qualquer coisa que seja declarada para lançar uma exceção verificada. Uma das idéias mais básicas (e vantagens) do tratamento de exceções é que, no caso de um erro, ele permite que camadas intermediárias de código que não têm nada a ver com um erro específico, ignorem completamente sua própria existência. As exceções verificadas (conforme implementadas em Java) não permitem isso, destruindo assim uma vantagem principal (a principal?) Do tratamento de exceções.

Em teoria, eles poderiam ter tornado as exceções verificadas úteis, aplicando-as apenas em todo o programa - ou seja, ao "construir" o programa, construa uma árvore de todos os caminhos de controle no programa. Vários nós na árvore seriam anotados com 1) exceções que eles podem gerar e 2) exceções que eles podem capturar. Para garantir que o programa estava correto, você caminhava na árvore, verificando se todas as exceções lançadas em qualquer nível da árvore eram capturadas em algum nível superior da árvore.

A razão pela qual eles não fizeram isso (eu acho, de qualquer maneira) é que isso seria terrivelmente difícil - na verdade, duvido que alguém tenha escrito um sistema de compilação que possa fazer isso por qualquer coisa, exceto extremamente simples (na fronteira com "brinquedos"). ") idiomas / sistemas. Para Java, coisas como o carregamento dinâmico de classes tornam ainda mais difícil do que em muitos outros casos. Pior, (ao contrário de algo como C) Java não é (pelo menos normalmente) estaticamente compilado / vinculado a um executável. Isso significa que nada no sistema realmente tem a consciência global de tentar executar esse tipo de aplicação até que o usuário final realmente comece a carregar o programa para executá-lo.

A aplicação da lei nesse ponto é impraticável por alguns motivos. Primeiro de tudo, mesmo na melhor das hipóteses, estenderia o tempo de inicialização, que já é uma das maiores e mais comuns reclamações sobre Java. Segundo, para fazer muito bem, você realmente precisa detectar o problema com antecedência suficiente para que um programador o conserte. Quando o usuário está a carregar o programa, é tarde demais para fazer muita coisa boa - suas únicas opções nesse ponto estão a ignorar o problema, ou se recusar a carga / executar o programa em tudo, na chance que não poderia ser uma exceção isso não foi pego.

Como são, as exceções verificadas são piores que inúteis, mas os projetos alternativos para exceções verificadas são ainda piores. Embora a idéia básica para exceções verificadas pareça boa, ela realmente não é muito boa e é um ajuste particularmente ruim para Java. Para algo como C ++, que normalmente é compilado / vinculado a um executável completo com nada como carregamento de classe dinâmica / de reflexão, você teria mais chances (embora, francamente, mesmo assim, aplicá-las corretamente sem o mesmo tipo de confusão que elas atualmente, a causa em Java provavelmente estaria além do estado atual da arte).


Sua primeira declaração inicial sobre ter que lidar com a exceção diretamente já é falsa; você pode simplesmente adicionar uma instrução throws, e essa instrução throws pode ser do tipo pai. Além disso, se você realmente acha que não precisa lidar com isso, basta convertê-lo em uma RuntimeException. Os IDE geralmente ajudam você nas duas tarefas.
Maarten Bodewes

2
@owlstead: Obrigado - eu provavelmente deveria ter sido mais cuidadoso com o texto lá. O ponto não era tanto o fato de você realmente ter capturado a exceção, mas todo nível intermediário de código precisa estar ciente de todas as exceções que possam fluir através dele. Quanto ao resto: ter uma ferramenta que facilita e agiliza a bagunça do seu código não muda o fato de estar bagunçando o código.
Jerry Coffin

As exceções verificadas destinavam-se a contingências - resultados previsíveis e recuperáveis ​​que não sejam sucesso, como distintos de falhas - problemas imprevisíveis de baixo nível / ou do sistema. FileNotFound ou erro ao abrir uma conexão JDBC foram considerados corretamente contingências. Infelizmente, os designers de bibliotecas Java cometeram um erro total e atribuíram todas as outras exceções de E / S e SQL à classe 'marcada'; apesar de serem quase completamente irrecuperáveis. literatejava.com/exceptions/…
Thomas W

@owlstead: As exceções marcadas só devem passar pelas camadas intermediárias da pilha de chamadas se forem lançadas nos casos em que o código de chamada intermediária espera . IMHO, as exceções verificadas teriam sido boas se os chamadores tivessem que pegá-las ou indicar se eram ou não esperadas ; exceções inesperadas devem ser agrupadas automaticamente em um UnexpectedExceptiontipo derivado de RuntimeException. Observe que "arremessos" e "não esperam" não são contraditórios; se foojoga BozExceptione paga bar, isso não significa que espera barjogar BozException.
22714

10

Eles são realmente bons para programadores irritantes e incentivam a ingestão de exceções. Metade das exceções é que você tem uma maneira padrão de lidar com erros e não precisa pensar em propagar explicitamente as condições de erro que não consegue lidar. (O padrão são: se você nunca manipular o erro em qualquer lugar do seu código, você efetivamente afirmou que isso não pode acontecer e ficará com uma saída sensata e um rastreamento de pilha, se estiver errado.) esta. Eles têm muita vontade de propagar explicitamente erros em C.


4
Reparo fácil: throws FileNotFoundException. Hard fix:catch (FileNotFoundException e) { throw RuntimeException(e); }
Thomas Eding

5

Acho justo dizer que as exceções verificadas foram um experimento fracassado. Onde eles deram errado é que eles quebraram as camadas:

  • O módulo A pode ser construído sobre o módulo B, onde
  • O módulo B pode ser construído sobre o módulo C.
  • O módulo C pode saber como identificar um erro e
  • O módulo A pode saber como lidar com o erro.

A boa separação dos envolvidos está quebrada, porque agora o conhecimento dos erros que poderiam ter sido limitados a A e C agora deve estar espalhado por B.

Há uma boa discussão sobre exceções verificadas na Wikipedia.

E Bruce Eckel também tem um bom artigo . Aqui está uma citação:

[Quanto mais regras aleatórias você adota sobre o programador, regras que não têm nada a ver com a solução do problema em questão, mais lento o programador pode produzir. E isso não parece ser um fator linear, mas exponencial

Acredito que no caso do C ++, se todos os métodos tiverem a throwscláusula de especificação, você poderá ter algumas otimizações agradáveis. Porém, não acredito que o Java possa ter essas vantagens, porque RuntimeExceptionsnão são verificadas. Um JIT moderno deve ser capaz de otimizar o código de qualquer maneira.

Um recurso interessante do AspectJ é a suavização de exceções: você pode transformar exceções verificadas em exceções não verificadas, especificamente para aprimorar as camadas.

Talvez seja irônico que o Java tenha passado por todo esse problema, quando poderia ter verificado null, o que poderia ter sido muito mais valioso .


haha, nunca pensei nessa verificação de coisa nula como uma decisão que eles não tomaram ... Mas finalmente entendi que NÃO é um problema inato depois de trabalhar no Objective-C, que permite que os nulos tenham métodos chamados.
Dan Rosenstark

3
A verificação nula é uma dor muito maior - você SEMPRE precisa verificar - além de não informar o motivo, qual é uma exceção bem conhecida.

5

Eu amo exceções.

Estou, no entanto, constantemente confuso com o uso inadequado deles.

  1. Não os use para manipulação de fluxo. Você não deseja que o código tente fazer alguma coisa e use uma exceção como gatilho para fazer outra coisa, porque as exceções são objetos que precisam ser criados e usam memória etc. etc. apenas porque você não se incomodou em escrever alguma coisa. tipo de verificação if () antes de fazer sua ligação. Isso é preguiçoso e dá um nome ruim à gloriosa exceção.

  2. Não os engula. Sempre. Em qualquer circunstância. No mínimo absoluto, você cola algo no seu arquivo de log para indicar que uma exceção ocorreu. Tentar rastrear seu caminho através de códigos que tragam exceções é um pesadelo. Mais uma vez, amaldiçoe seus engolidores de exceções por colocar a poderosa exceção em descrédito!

  3. Trate as exceções com seu chapéu OO. Crie seus próprios objetos Exception com hierarquias significativas. Camada de sua lógica de negócios e suas exceções de mãos dadas. Costumava descobrir que, se eu iniciasse em uma nova área de um sistema, encontraria a necessidade de subclassificar Exception dentro de meia hora após a codificação. Hoje em dia, começo escrevendo as classes Exception.

  4. Se você estiver escrevendo bits de código 'frameworky', verifique se todas as suas interfaces iniciais foram gravadas para lançar suas próprias novas exceções, quando apropriado ... e verifique se os codificadores têm algum código de amostra no lugar do que fazer quando o código lança uma IOException, mas a interface na qual eles estão gravando deseja lançar uma 'ReportingApplicationException'.

Depois de entender como fazer as exceções funcionarem para você, você nunca mais olhará para trás.


2
+1 para obter boas instruções. Além disso, quando apropriado, use initCauseou inicialize a exceção com a exceção que a causou. Exemplo: você possui um utilitário de E / S e precisa apenas de uma exceção se a gravação falhar. No entanto, existem vários motivos pelos quais uma gravação pode falhar (não foi possível obter acesso, erro de gravação etc.) Lance sua exceção personalizada com a causa, para que o problema original não seja engolido.
Michael K

3
Eu não quero ser rude, mas o que isso tem a ver com exceções CHECKED? :)
palto 12/06

Pode ser reescrito para escrever sua própria hierarquia de exceções verificadas .
Maarten Bodewes

4

As exceções verificadas também estão no ADA.

(Atenção, esta postagem contém crenças muito fortes que você pode encontrar em confronto.)

Os programadores não gostam deles e reclamam, ou escrevem códigos de exceção para engolir.

Exceções marcadas existem porque as coisas não só podem falhar no trabalho, você pode fazer uma análise do modo / efeitos de falha e determinar isso com antecedência.

A leitura do arquivo pode falhar. Chamadas RPC podem falhar. E / S de rede pode falhar. Os dados podem ser mal formatados quando analisados.

O "caminho feliz" para o código é fácil.

Eu conhecia um cara da Universidade que sabia escrever um ótimo código de "caminho feliz". Nenhum dos casos extremos funcionou. Atualmente, ele faz Python para uma empresa de código aberto. Nuff disse.

Se você não deseja lidar com exceções verificadas, o que realmente está dizendo é

While I'm writing this code, I don't want to consider obvious failure modes.

The User will just have to like the program crashing or doing weird things.

But that's okay with me because 

I'm so much more important than the people who will have to use the software

in the real, messy, error-prone world.

After all, I write the code once, you use it all day long.

Portanto, as exceções verificadas não serão apreciadas pelos programadores, porque isso significa mais trabalho.

Obviamente, outras pessoas podem querer que esse trabalho seja feito.

Eles podem ter desejado a resposta certa, mesmo se o servidor de arquivos falhar / o pen drive morrer.

É uma crença estranha na comunidade de programação que você deveria usar uma linguagem de programação que facilite sua vida e que você goste quando seu trabalho é escrever software. Seu trabalho é resolver o problema de alguém, não permitindo que você se envolva na improvisação programática do Jazz.

Se você é um programador amador (não está programando por dinheiro), sinta-se à vontade para programar em C # ou em outro idioma sem exceções verificadas. Heck, recorte o intermediário e programe no Logo. Você pode desenhar padrões bonitos no chão com a tartaruga.


6
Bom discurso retórico que você chegou lá.
Adam Lear

2
+1 "Nenhum dos casos extremos funcionou. Atualmente, ele faz Python para uma empresa de código aberto. Nuff disse." Classic
NimChimpsky

1
@palto: Java obriga a considerar o caminho não fácil. Essa é a grande diferença. Para C #, você pode dizer: "A exceção está documentada" e lave suas mãos. Mas os programadores são propensos a erros. Eles esquecem as coisas. Quando escrevo código, quero ser 100% lembrado de qualquer falha que um compilador possa me informar.
Thomas Eding

2
@ThomasEding Java me obriga a lidar com a exceção em que o método é chamado. É muito raro que eu realmente queira fazer algo sobre isso no código de chamada e, geralmente, quero destacar a exceção da camada de tratamento de erros do meu aplicativo. Em seguida, eu tenho que remediá-lo com a quebra da exceção em uma exceção de tempo de execução ou eu tenho que criar minha própria exceção. Programadores preguiçosos têm uma terceira opção: apenas ignore, porque eu não ligo. Dessa forma, ninguém sabe que o erro aconteceu e o software tem bugs realmente estranhos. Esse terceiro não acontece com exceções não verificadas.
palto

3
@ThomasEding Fazer isso mudará a interface de tudo acima, revelando desnecessariamente os detalhes da implementação para todas as camadas do aplicativo. Você só pode fazer isso se a exceção for algo que você deseja recuperar e fizer sentido na interface. Não quero adicionar uma IOException ao meu método getPeople porque algum arquivo de configuração pode estar ausente. Isso simplesmente não faz sentido.
palto 11/10

4

As exceções verificadas devem ter incentivado as pessoas a lidar com erros próximos à sua origem. Quanto mais longe da fonte, mais funções terminam no meio da execução e maior a probabilidade de o sistema entrar em um estado inconsistente. Além disso, é mais provável que você pegue a exceção errada por acidente.

Infelizmente, as pessoas tendem a ignorar exceções ou a adicionar especificações de lançamento cada vez maiores. Ambos não entendem o que as exceções verificadas devem encorajar. Eles são como transformar código processual para usar muitas funções Getter e Setter, supostamente tornando-o OO.

Essencialmente, as exceções verificadas estão tentando provar um certo grau de correção no seu código. É praticamente a mesma idéia que a digitação estática, na medida em que tenta provar que certas coisas estão corretas antes que o código seja executado. O problema é que toda essa prova extra exige esforço e vale a pena?


2
"Ignorar exceções" geralmente está correto - muitas vezes a melhor resposta a uma exceção é deixar o aplicativo travar. Para uma base teórica para esse ponto de vista, leia Construção orientada a objetos de Bertrand Meyer . Ele mostra que, se as exceções são usadas corretamente (para violações de contrato), existem basicamente duas respostas corretas: (1) deixe o aplicativo travar ou (2) corrija o problema que causou a exceção e reinicie o processo. Leia Meyer para detalhes; é difícil resumir em um comentário.
Craig Stuntz

1
@ Craig Stuntz, ignorando as exceções, eu quis dizer engoli-las.
Winston Ewert

Ah, isso é diferente. Eu concordo que você não deve engoli-los.
Craig Stuntz

"Ignorar exceções geralmente está correto - muitas vezes a melhor resposta a uma exceção é deixar o aplicativo travar.": Depende realmente do aplicativo. Se você tiver uma exceção não detectada em um aplicativo Web, o pior que pode acontecer é que seu cliente relate uma mensagem de erro em uma página da Web e você poderá implantar uma correção de bug em alguns minutos. Se você tiver uma exceção enquanto um avião estiver pousando, é melhor lidar com isso e tentar se recuperar.
Giorgio

@ Giorgio, muito verdade. Embora eu queira saber se, no caso de avião ou similar, se a melhor maneira de recuperar é reiniciar o sistema.
Winston Ewert
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.