Checked e unchecked exceptions nas camadas

[quote=Emerson Macedo]…
Curioso é você definir que quem manda mensagem para teu objeto deva tratar alguma coisa. Eu acho isso péssimo. Acho muito mais lógico eu notifica-lo que algum problema aconteceu (i.e. lançando uma exception) e ele decidir qual ação vai tomar dado a mensagem de retorno. É o caso da SQLException. Quem a criou “definou” que agente tinha que tratar e no final das contas agente viu no que deu.[/quote]

Acho que nossa discussão entrou em um loop aqui. :slight_smile: Concordo com o que o Rubem disse. SQLException não é um bom exemplo e não acho que deve ser uma checked exception.

[quote=Emerson Macedo]
Curioso é você definir que quem manda mensagem para teu objeto deva tratar alguma coisa. Eu acho isso péssimo. Acho muito mais lógico eu notifica-lo que algum problema aconteceu (i.e. lançando uma exception) e ele decidir qual ação vai tomar dado a mensagem de retorno. É o caso da SQLException. Quem a criou “definou” que agente tinha que tratar e no final das contas agente viu no que deu.[/quote]

Hum, tem o outro lado da moeda também… Por um lado checked exceptions tira a liberdade de quem usa a classe pois obriga a tratar a excessão, por outro lado se não tiver checked exceptions o autor da classe não tem nenhum mecanismo para obrigar quem usa a sua classe a tratar uma determinada excessão que ele julga importante ser tratada.

O negócio é o seguinte:

Método A pode lançar exception X ocasionalmente. Vamos supor que o lançamento de X não corresponde a um erro de programação e nem a alguma falha catastrófica irrecuperável e que seja algo que possa ser efetivamente tratado de uma forma que não seja simplesmente “gere um log” ou “mostre uma mensagem para o usuário”. Ou seja, algo que cai bem no conceito de uma exceção verificada.

Chamar o método A e não tratar um possível X é um erro de programação. Logo, seria bom que o compilador te forçasse a tratar.

Por outro lado, se X for unchecked, é muito fácil se esquecer de tratar X corretamente. E isso é muito pior do que ter que encapsular em outras exceções ou coisas assim.

Ou pior, vamos supor que você pode ter DataDeVencimentoInvalidaException sugerido pelo Rubem. Vamos supor que ela é unchecked, daí você chama um método que lança esse troço e você nem sabia que isso podia ser lançado.

Exceções não checadas correspondem a falhas catastróficas e erros de programação. As outras, devem ser tratadas adequadamente, mesmo que isso exija cláusulas catch bem chatas e repetitivas. Se não houvesse checked exceptions, mesmo as falhas normais e esperadas poderiam compremeter a aplicação por não serem tratadas corretamente. E frequentemente vemos problemas por ter exceções que são esperadas serem empacotadas em unchecked exception ou herdarem de RuntimeException.

C# não tem checked exception. Lá é comum o problema do tipo “ué, eu não sabia que esse método podia lançar essa exceção.”

[quote=Rubem Azenha][quote=Emerson Macedo]
Curioso é você definir que quem manda mensagem para teu objeto deva tratar alguma coisa. Eu acho isso péssimo. Acho muito mais lógico eu notifica-lo que algum problema aconteceu (i.e. lançando uma exception) e ele decidir qual ação vai tomar dado a mensagem de retorno. É o caso da SQLException. Quem a criou “definou” que agente tinha que tratar e no final das contas agente viu no que deu.[/quote]

Hum, tem o outro lado da moeda também… Por um lado checked exceptions tira a liberdade de quem usa a classe pois obriga a tratar a excessão, por outro lado se não tiver checked exceptions o autor da classe não tem nenhum mecanismo para obrigar quem usa a sua classe a tratar uma determinada excessão que ele julga importante ser tratada.[/quote]
Realmente entramos em loop. Você acha que um objeto pode obrigar seu cliente a tratar uma exception. Eu já acho que o cliente que mandou a mensagem ao objeto e recebeu esse retorno deve decidir o que fazer.

Se você tiver testes automatizados (e.g. unitarios, integrados e funcionais), certamente não se esquecerá de tratar quando for necessário.

Se você tiver testes automatizados (e.g. unitarios, integrados e funcionais), certamente não se esquecerá de tratar quando for necessário.[/quote]

Seguindo isso que você disse, poderíamos tirar todas as verificações de corretude que o compilador faz e deixar tudo isso ao cargo do programador e de seus testes unitários/funcionais/whatever.

Aí é que esta a beleza da computação :slight_smile:

Eu prefiro ter X e eu preferir não usar X do que eu querer usar X mas não poder usar X por que a linguagem não tem X.

Depende… ter testes não garante que o teste faz a validação adequada.
Concordo que de forma alguma checked exceptions substituem ou diminuem a importância de testes, mas elas pelo menos dão um feedback mais rápido.

[quote=Rubem Azenha][quote=peczenyj]
Ok, como vc lidaria se SQLException fosse unchecked?
[/quote]

SQLException é um típico caso em que a API não foi bem projetada. Erros como SQLException, HibernateException, IOException, e etc são erros que não tem como ser remediado e geralmente o usuário do sistema não tem controle sobre a situação que gerou o erro, diferente de DataDeVencimentoInvalidaException ou DataSelecionaEhDiaUtilException (nome horrivel, eu admito).
[/quote]

Remediar o problema não tem nada a haver com ser verificada ou não. Não é porque a exceção é verificada que significa que vc pode resolver o problemas. E também não é verdade em exceções não-verificadas.

A diferença entre os dois tipos é conceptual, logo não faz sentido discutir se seria possivel usar java apenas com exceções não-verificadas. É possivel. Tanto é possivel que exceções verificadas só existem em java. Em outras linguagens ninguem sabe o que é isso.

Exceções verificadas são uteis porque obrigam o caller ( quem chama o método) a lidar ( handle, manipular) com a exceção explicitamente. Ou seja, o programador não tem como não saber que ali ha o lançamento de uma exceção.
Isto é particularmente util quando vc está criando frameworks e /ou API de camada. Quando o caller invoca a camada /framework ele perde o controlo. Nesse caso a camada abaixo tem que vigiar que é bem usada. Tal como um método verifica se argumentos não são nulos e estão dentro do esperado, também o mesmo tem que ser feito para exceções. Este tipo de API tem que forçar o caller a lidar com a exceção de forma que lidar com a exceção faz parte do contrato. É chato é, mas é util ? sim. muito. Não tem como vc se esquecer de lidar com a exceção.

Agora, lidar e resolver são coisas diferentes. O caller pode não saber resolver o problema. Nesse caso ele passará a exceção adiante. Mas agora ele é livre de dar tratamento À exceção. Por exemplo, encapsulando-a numa exceção não-verificada.

As API SQL , IO , Threads e Reflection não são mal projetadas. Isso é um mito levantado e alimentado por quem não sabe trabalhar com exceções. Elas usam exceções verificadas porque são API que ao serem chamadas o programador perde o controlo do que acontece ( aka, são chamadas “nativas” ).

As exceções do Hibernate ou do EJB poderiam não ser verificadas. Ai éuma decisão de design, mas não podemos censurar a escolha de usar exceções verificadas só porque temos preguiça de lidar com elas.

Na realidade , porque a maioria dos programadores não divide o programa em camadas , têm que lidar com exceções veirifcadas em todo o lugar da aplicação ( tipo tratar um SQLException dentro de um component siwing). Esse é que é o problema e não as exceções serem verificadas. Se encapsular o uso de SQL em uma camada, de arquivos em uma camada , de reflectino ,etc… nunca mais vai ter problemas com exceções verificadas.

As exceções verificadas são essenciais para um bom design da aplicação, especialmente se ela for um API de uso genérico.
Sem elas é possivel utilizar as mesmas funções da API mas com uma garantia a menos.

Na época em que querem criar anotações @NotNull para verificação automática da passagem de nulos não autorizados, não faz sentido apoiar a ideia de que exceções verificadas são inuteis. Quando mais facilidades existirem para construir bons designs de forma elegante, melhor.

Não digam que a API de SQL foi mal projetada porque usa exceções verificadas que isso pega mal. Ela foi mal projetada sim, mas em outros pontos. Por exemplo, falta de hierarquia de exceções mais fina (embora isso esteja sendo solucionado).

Se você tiver testes automatizados (e.g. unitarios, integrados e funcionais), certamente não se esquecerá de tratar quando for necessário.[/quote]

Seguindo isso que você disse, poderíamos tirar todas as verificações de corretude que o compilador faz e deixar tudo isso ao cargo do programador e de seus testes unitários/funcionais/whatever.[/quote]
Isso não tem nada ver com que eu disse, apesar de poder ser aplicado em alguns casos como na utilização de linguagens de tipagem dinâmica (neste caso o compilador não verifica os tipos). Mas voltando o que eu havia dito, se você fizer bons testes automatizados, provavelmente você vai testar os fluxos que você espera em cada caso.

Depende… ter testes não garante que o teste faz a validação adequada.
Concordo que de forma alguma checked exceptions substituem ou diminuem a importância de testes, mas elas pelo menos dão um feedback mais rápido.[/quote]
Ter bons testes e uma boa cobertura destes te dá uma boa garantia. Quanto ao feedback mais rápido é questionável. Eu acredito que usando uma abordagem TDD meu feedback vem mais rápido através dos testes.

Então como vcs tratariam IOException? querendo ou não estamos sujeitos a erros inesperados o mesmo se aplica ao SQLException.
Semana passada tive um erro bem chato, o desenvolvedor definiu o limite da sequence em 999 e os campos que sâo alimentados pela sequence numeric(3) chegou um belo dia que chegamos no numero magico 1000 e desenvolvedor não tratou esta situação e o incrivel que driver da oracle tbm não tratava essa situação não lançava um “ORA:0XXX”.

Se tivesse tratado pelo menos com um SQLException a exceção seria mais obvia não igual a esta abaixo:
Erro da sequence

Erro do sistema: null
com.a.AException
	at com.a.ejbBase.ejbStoreBase(ejbBase.java:331)
	at applications.avaliacaoPd.Projeto.ejbStore(Projeto.java:282)
	at applications.avaliacaoPd.admin.AvaliacaoAdmin.doGet(AvaliacaoAdmin.java:190)
	at applications.avaliacaoPd.admin.AvaliacaoAdmin.doPost(AvaliacaoAdmin.java:39)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
	at com.a.ServletBase.service(ServletBase.java:78)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
	at org.apache.tomcat.core.ServletWrapper.doService(ServletWrapper.java:404)
	at org.apache.tomcat.core.Handler.service(Handler.java:286)
	at org.apache.tomcat.core.ServletWrapper.service(ServletWrapper.java:372)
	at org.apache.tomcat.core.ContextManager.internalService(ContextManager.java:797)
	at org.apache.tomcat.core.ContextManager.service(ContextManager.java:743)
	at org.apache.tomcat.service.connector.Ajp12ConnectionHandler.processConnection(Ajp12ConnectionHandler.java:166)
	at org.apache.tomcat.service.TcpWorkerThread.runIt(PoolTcpEndpoint.java:416)
	at org.apache.tomcat.util.ThreadPool$ControlRunnable.run(ThreadPool.java:498)
	at java.lang.Thread.run(Thread.java:534)

Tbm concordo com opinião do Victor, acho que as exceções checked são utilizadas de forma errada.

[quote=ramilani12]Então como vcs tratariam IOException? querendo ou não estamos sujeitos a erros inesperados o mesmo se aplica ao SQLException.
Semana passada tive um erro bem chato, o desenvolvedor definiu o limite da sequence em 999 e os campos que sâo alimentados pela sequence numeric(3) chegou um belo dia que chegamos no numero magico 1000 e desenvolvedor não tratou esta situação e o incrivel que driver da oracle tbm não tratava essa situação não lançava um “ORA:0XXX”.

Tbm concordo com opinião do Victor, acho que as exceções checked são utilizadas de forma errada.[/quote]
Partindo desse princípio que você disse, todas as exceptions deveriam ser checked pra não haver erros inesperados …
No mínimo seu sistema deve ter um tratador na última camada antes da apresentação que logue esse erro e apresente uma mensagem amigável para o usuário. A partir do conhecimento desse “novo erro inesperado”, você pode avaliar se precisa alterar seu sistema para trata-lo de alguma forma ou se esse tipo de erro não será possível ser tratado e o melhor a fazer mesmo é o descrito anteriormente.

Ermerson mas se tratando de IOException e SQLException

Sim, essas são as piores IMO. Não tem nem como se recuperar. Muito melhor é ter um tratamento genérico, logar e apresentar uma mensagem bem nice pro usuário. Não entendi muito bem a sua dúvida. Qual seria o ponto?

Então entramos num acordo aqui.

Clareando as idéias:

O cliente, para o bem dele, deve tratar exceções que o objeto a quem ele chamou lança.

A questão é se ele deve ser obrigado a fazer isto ou não, onde a obrigação se traduz para checked exceptions, a desobrigação para unchecked exceptions.

Pois bem, então é tudo questão de design?

Eu acho que o objeto chamado deve ter um modo de avisar (leia-se: warnings) quem o chama que este deve lidar com suas exceções, porém que este não deva ser obrigado a fazer isso, como Java o faz hoje.

Digamos que checked exceptions não precisem mais de blocos de try-catch, mas sim que o compilador gere diversos warnings, avisando que os métodos chamados devem ser tratados. É uma boa solução? unchecked exceptions continuariam como estão hoje, sem avisar ninguém. throws passariam os warnings p/ cima.

É uma ideia interessante. Hoje em dia se soluciona isso facilmente quando quem criou a classe que lança a exception documenta no javadoc as exceptions que podem ser lançadas em cada caso. Dessa forma, mesmo sendo unchecked, o cliente da classe vai saber o que precisa tratar.

Então entramos num acordo aqui.

Clareando as idéias:

O cliente, para o bem dele, deve tratar exceções que o objeto a quem ele chamou lança.

A questão é se ele deve ser obrigado a fazer isto ou não, onde a obrigação se traduz para checked exceptions, a desobrigação para unchecked exceptions.

Pois bem, então é tudo questão de design?

Eu acho que o objeto chamado deve ter um modo de avisar (leia-se: warnings) quem o chama que este deve lidar com suas exceções, porém que este não deva ser obrigado a fazer isso, como Java o faz hoje.

Digamos que checked exceptions não precisem mais de blocos de try-catch, mas sim que o compilador gere diversos warnings, avisando que os métodos chamados devem ser tratados. É uma boa solução? unchecked exceptions continuariam como estão hoje, sem avisar ninguém. throws passariam os warnings p/ cima.[/quote]

Colaborando com o assunto; achei a sua idéia muito interessante, pois tira a obrigação de
usar try-catch todas as vezes deixando opcional, mas avisando dos riscos que este usuario corre.O que vejo ocorrer geralmente e que as pessoas se cansam de tratar as checked exceptions e acabam no final fazendo isso:

try {
			
		} catch (Exception e) {
			
		}

O que faz o mecanismo de exceções perderem todo o sentido!

Mas na outro lado da história, existem casos onde as checked exceptions são bem empregadas,como já citaram acima nas famosas IOException. Um exemplo é a FileNotFoundException, essa exceção deve ser checked pois o programador tem que ser obrigado a fornecer um tratamento caso um arquivo não seja encontrado, pois do contrário correrá o risco de jogar o stacktrace na “cara” do usuário.

No geral, são poucos casos onde o uso de checked exceptions são bem empregadas.

Os casos são poucos devido à falta de qualidade do design das aplicações.
Os desenvolvedores não pensam nesse problema o suficiente e quando pensam não o resolvem adequadamente.
A falta de seguimento das diretrizes de programação (aka boas práticas) e muitas vezes a falta de disciplina do programador ou da equipe levam o códigos como o do seu exemplo.

Se o tratamento de exceção está virando um problema é porque - provavelmente - não está sendo bem feito.
Não é natural que codigo de try-catch esteja espalhado pela aplicação toda.
Por outro lado, também não é natural que código que se repete ( o codigo no catch se repete bastante) não seja encaspulado em um método.

O padrão ExceptionHandlerajuda a resolver o problema. É especialmente util na presença de IOException e SQLException e pode converter qualquer exceção para uma não-verificada tipada : coisa que, tb, normalmente, o desenvolvedor não faz.

Trabalhar com exceções não-verificadas é muito mais simples, mas para que dê certo é preciso que elas sejam fortemente tipadas.
Ou seja, uma FileNotFoundException não pode virar uma RuntimeException. Tem que virar uma RuntimeFileNotFoundException filhe da RuntimeException.

Se seguirmos as boas práticas para lidar com exceções os problemas vão embora e podemos começar a apreciar a beleza e o visionarismo que existe nas exceções verificadas.

[quote=Bruno Laturner]Lendo o Mundo Java 33, sobre EJBs e Spring, na parte de controle de transações, fala que ambas as tecnologias fazem rollback da transação quando encontram unchecked exceptions, mas com as checked deixam rolar, supõem que o desenvolvedor queria que isso aconteça.

Fiquei pensando no caso de uma exceção na camada de persistência, como uma SQL ou IOException, normalmente não são esperadas pela aplicação.

A camada deveria tratar esses erros e transformá-los em erros do sistema quando a requisição voltar para a camada que a chamou? Acho meio infeliz dar um catch e um throw logo depois só pra transformar a exceção. O que tem de “checado” numa exceção dessas?[/quote]

Só um adendo meio atrasado, tanto o Spring como o JPA vão empacotar automaticamente todas as IO e SQL exceptions que surjam no seu código, então eles não vão lançar checked exceptions, quem lança é o seu próprio código, por isso que as ferramentas não lidam com isso.

Pergunta rápida (ou nem tanto):

Exceções” da aplicação, por exemplo, caso você for procurar por uma pessoa pelo CPF, não encontrá-la e querer mostrar uma mensagem de “erro” ‘Pessoa não encontrada’ na tela.

Pensemos que é normal você não encontrar alguém, portanto não seria uma exceção, mas como vocês normalmente fazem para jogar essa mensagem lá dos confins interiores do sistema para uma JSP?

Pergunto por que uma solução que conheço é levantar um RuntimeException e deixar ele ir até a servlet, onde tem um código que vai fazer o logging e redirecionar p/ uma página de erro genérica com uma mensagem. O chato é ele ficar gerando log por coisas que não são exceções.

Tem uma solução mais elegante e ainda prática?

Se é normal você não encontrar alguém, não acho que seja bom lançar uma exception. Acho que o melhor é você retornar algo que indique que a pesquisa não retornou nada.

Em Java, pode retornar null, mas tem que tomar cuidado com NPE. Nesse caso, você vai ter que verificar se a referência é null e exibir a mensagem conforme necessário (você pode usar NullObject também se for o caso). Quando a pesquisa retorna uma lista a boa pratica é retornar a lista vazia ao invés de null.

Não, retornar nulos e checar por NPEs já faço, a questão mesmo é sobre usar exceções para exibir mensagens do programa numa página. Pergunto se isso é normal.