Exceções do Java são úteis: talvez você é que não saiba usá-las
Tags: java, ptbr, braziliandevs, tutorial
Published: 2021-06-18 13:16:50

---
title: Exceções do Java são úteis: talvez você é que não saiba usá-las
published: true
description: Exceções do Java são úteis: talvez você é que não saiba usá-las
tags: java, ptbr, braziliandevs, tutorial
cover_image: https://apexensino.com.br/wp-content/uploads/2020/05/26-java-logo.jpg
---
Semana passada <a title="Java é simples: você que burocratiza seus projetos" href="https://devkico.itexto.com.br/?p=2208">escrevi</a> sobre como os programadores complicam o código Java. Volto este tema agora para falar um pouco sobre um recurso extremamente útil da linguagem que, acredito, é muito mal compreendido: as exceptions.
<h2>Uma visão histórica</h2>
Acredito que para dominar uma linguagem de programação um dos primeiros passos que devemos tomar é buscar entender o que motivou e quais princípios guiaram sua criação. Uma abordagem histórica cai muito bem neste momento, sendo assim recomendo que você leia o capítulo sobre exceções tal como redigido na especificação do Java 1.0, acessível neste <a href="https://msdn.microsoft.com/en-us/library/aa277910(v=vs.60).aspx">link</a>&nbsp;(observem a ironia).

A leitura desta especificação é&nbsp;<strong>muito</strong> interessante, especialmente quando vemos de forma explícita os principais objetivos dos designers na criação da linguagem naquele momento:&nbsp;<em>prover portabilidade e robustez</em>. Ao falarmos de exceções o que realmente nos interessa é o segundo princípio. Como esta robustez é obtida?

Antes um pouco de semântica: uma exception, como o próprio nome já nos diz, denota uma condição anormal que ocorre durante o fluxo de execução dos nossos programas.

&nbsp;
<h3>A vida sem exceptions</h3>
Muitas linguagens de programação simplesmente finalizam a execução do software (pense em C ou Pascal) quando algo assim ocorre e não há alguma forma de tratamento do erro padronizada, outra alternativa é apenas retornar um código de erro que pode facilmente ser ignorado pelo programador (pense na função <strong><a href="https://msdn.microsoft.com/en-us/library/wyssk1bs.aspx">read</a></strong><a href="https://msdn.microsoft.com/en-us/library/wyssk1bs.aspx"> do C </a>retornando o valor -1, por exemplo).

Vamos a um exemplo rápido usando "pseudo C". A linguagem possuí uma função chamada read (mencionada acima) que lê bytes em uma fonte de dados e a armazena em um buffer. Ela retorna o valor -1 caso algo de errado ocorra, 0 se chegamos ao final do arquivo e um valor positivo nos informando quantos bytes foram lidos. Veja o código abaixo:

```c
char buffer[128];
read(arquivo, buffer, 128);
printf("Serei impresso?");
// operações importantes seriam executadas na sequência
```

Este é um código muito comum: o programador espera que o arquivo&nbsp;<strong>sempre</strong> exista, sendo assim, possuí a "certeza" de que a saída "Serei impresso?" sempre irá ser exposta em seu terminal. Mas nem sempre é assim: e se o arquivo imaginário sumir? Nosso ingênuo programador irá enfrentar problemas pois a execução do seu programa terminaria em algum ponto após esta impressão.

Talvez nosso programador pudesse escrever o código acima de uma forma diferente tal como no exemplo a seguir:

```c
char buffer[128];
int resultadoLeitura = read(arquivo, buffer, 128);
if (resultadoLeitura &lt; 0) {
// tento corrigir a situação aqui
}
```
É uma alternativa, o código se tornou mais robusto, mas&nbsp;sua leitura&nbsp;não torna&nbsp;claro <strong>o que de fato ocorreu</strong>&nbsp;para termos um erro. O arquivo foi apagado? Seria um problema de permissão? Ainda pior: o código que motivou a escrita do programa, o fluxo principal (em condições ideais de temperatura e pressão) agora se encontra mesclado ao código de tratamento de erros.

Java veio com uma solução mais interessante. Visto que nossas classes são uma interface, por que não alertar&nbsp;seus clientes a respeito do que pode dar errado e, ainda melhor: força-los a tratar estas situações (o problema está neste "força-los")?
<h2>Pensando como Gosling, Joy e Steele</h2>
<img class="aligncenter  wp-image-2213" src="https://devkico.itexto.com.br/wp-content/uploads/2015/07/java_language_spec-239x300.jpg" alt="java_language_spec" width="169" height="212">
<h3>Clareza na escrita</h3>
Por mais incrível que possa parecer a diversos críticos atuais da linguagem, naquela época um dos objetivos era ter código menos verboso. O ideal é que o programador pudesse ver o fluxo principal do seu programa de uma forma simples, e o tratamento dos erros isoladamente, tal como no exemplo a seguir:

```java
String conteudoArquivo(File arquivo) {
 try {
// meu fluxo principal entra aqui
 } catch (FileNotFoundException;ex) {
// o que eu faço se o arquivo não existir?
} catch (EOFException ex) {
 // e se o arquivo chegar ao fim antes do imaginado?
} catch (IOException ex) {
 // e se for algum outro erro de I/O que não previ e
// não seja como os que mostrei antes?
}
}
```

É interessante como agora você sabe&nbsp;<strong>o quê</strong> pode ter dado errado, e consegue diferenciar de forma clara como tratar cada uma daquelas situações. Ainda melhor: o que realmente importa, o fluxo principal, está claramente isolado.

Uma exception é na realidade um desvio de fluxo. Talvez você lide com erros do tipo FileNotFound e IOException da mesma forma. Neste caso, como a primeira exception é uma subclasse da segunda, basta colocar um único bloco catch para esta.
<h3>Exceptions como contrato</h3>
&nbsp;

Mais do que isto, acredito que muitos programadores simplesmente não saibam interpretar o código que encontram: imagine uma declaração de método como a abaixo:

```java
void processeArquivos(File[] arquivos) throws FileNotFoundException
```

O método me diz:
<blockquote>"Recebo uma lista de arquivos em uma matriz como parâmetro.&nbsp;Conseguirei executar meu trabalho quando todos os arquivos forem acessíveis a mim. Se me passar algum deles que não seja, repasso a você, que me chamou, a responsabilidade de lidar com este problema para mim."</blockquote>
O método, é um&nbsp;<em>contrato</em>, e a exception, uma validação de que o mesmo será cumprido. Se não for o caso, o fluxo deverá ser alterado para que o seja ou a responsabilidade para se resolver o problema, repassada a outro objeto (talvez o cliente do cliente).

Mais do que isto: um contrato válido é aquele bem definido. Fica fácil perceber quando quem escreveu o código não tem muita ideia a respeito do que está fazendo. Observe a declaração de método abaixo:

```java
void processeArquivos(File[] arquivos) throws Throwable
```

O que este método me diz?
<blockquote>"Recebo uma lista de arquivos para serem processados, alguma coisa pode dar errado, mas não sei o que."</blockquote>
Temos um meio contrato aqui: apenas sabemos que devemos enviar arquivos para este método. Não sei se todos devem realmente estar acessíveis, apenas os envio.
<h3>Exceptions checadas e não checadas. Pra quê?</h3>
Por que há as tais "checked exceptions" e "unchecked exceptions"? O que diferencia uma de outra? Uma interpretação rápida seria:
<blockquote>"Checked exception é aquela que é uma subclasse de java.lang.Exception e que, se eu disparar no corpo do meu método, tenho de incluir uma clausula throws. A outra não, eu apenas a disparo lá dentro e não aviso ninguém a respeito pois é uma subclasse de RuntimeException ou Error."</blockquote>
O que não responde quase nada além de expor uma hierarquia de classes incompleta. A resposta é mais simples: há erros que são tratáveis e outros nem tanto. Erros tratáveis são aqueles que definem um contrato e os clientes conseguem ao menos tentar resolve-los&nbsp;quando ocorrem.

Por exemplo: um arquivo inacessível é um erro tratável. Se topei com um erro do tipo FileNotFound, talvez seja possível criar um novo arquivo&nbsp;para em seguida chamar aquela função ou procedimento novamente.

Por outro lado, se houver um crash do meu sistema operacional ou meu sistema de arquivos desaparecer, não há muito o que eu possa fazer. É um erro de tempo de execução (runtime). E a quantidade de problemas deste tipo que podem ocorrer é praticamente infinita: seu HD pode pegar fogo, ou seu HD pode ser removido, ou seu sistema operacional pode desaparecer, ou sua rede pode se tornar inacessível, ou alguém pode desligar o servidor, ou....

Por que as exceções do tipo Runtime não são "checked"? Vou pedir para Gosling, Joy e Steele uma força. Veja o que é dito na seção <a href="http://titanium.cs.berkeley.edu/doc/java-langspec-1.0/11.doc.html#44149">11.2.2 da especificação</a>:
<blockquote>"A informação disponível para o compilador Java, e o nível de análise que este executa, raramente são &nbsp;suficientes para se descobrir&nbsp;que erros de tempo de execução poderão ocorrer, mesmo sendo óbvio para o programador. Obrigar o programador a declarar todas estas exceções seria apenas uma tarefa irritante para o desenvolvedor." (tradução minha)</blockquote>
É interessante também ver o que os autores dizem na especificação ao nos dizerem<a href="http://titanium.cs.berkeley.edu/doc/java-langspec-1.0/11.doc.html#44147"> por que a outra categoria de erros (java.lang.Error) não são checados</a>&nbsp;(11.2.1):
<blockquote>"São problemas que podem ocorrer em inúmeros pontos de um programa e cuja solução é difícil ou impossível. Um programa escrito em Java que precisasse lidar com todos estes erros seria uma zona e sem sentido algum" (tradução minha)</blockquote>
Sendo assim, ao invés de obrigar o desenvolvedor a tratar&nbsp;<strong>cada um</strong> destes problemas, por que não força-lo a lidar apenas com o que <b>pode ser tratado</b>? Este é um dos principais motivadores: te forçar a escrever menos código e, quem sabe, escrever código de melhor qualidade.

Isto não quer dizer que você deva escrever código como o a seguir:

```java
try {
// aqui está meu fluxo principal
} catch (Throwable t) {
// aqui lidarei com todos os problemas possíveis e impossíveis
// dos multiversos
}
```

Quando escrevemos algo como "catch (Throwable)" estamos com uma das seguintes ideias na cabeça:
<ul>
 	<li>Vou ignorar qualquer tipo de erro que venha a ocorrer.
(me faz lembrar do "<a href="https://msdn.microsoft.com/pt-br/library/5hsw66as.aspx">on error resume next</a>" do VB)</li>
 	<li>Todos os erros são iguais, sendo assim os tratarei todos da mesma forma.</li>
</ul>
Se um dos princípios norteadores da criação do Java foi a robustez, e estamos usando&nbsp;Java (ignore sua linguagem favorita por um momento), escrever código deste tipo é corromper a linguagem e se induzir ao erro.

Mais do que isto: checked exceptions permitem ao&nbsp;<strong>compilador</strong> verificar se você está lidando com as situações anômalas definidas no contrato das suas interfaces.
<h2>Como uso bem as exceções?</h2>
O principal motivador para a escrita deste post são as críticas que ouço a respeito do modo como a linguagem Java lida com exceções. É interessante como muitas pessoas se esquecem que o recurso foi incluído na linguagem para facilitar a vida do programador, e não complicá-la.

Curiosamente, a esmagadora maioria das críticas que vejo são motivadas pelo mal uso ou compreensão do recurso. Sendo assim, seguem algumas dicas:
<ul>
 	<li>Pense na declaração de exceções como a definição de um contrato bem definido: elas definem premissas, ou seja, aquilo que&nbsp;<strong>não deve ocorrer</strong> para que o código possa ser executado com sucesso.</li>
 	<li>Tire proveito da precisão: um "catch (Throwable)" não te possibilita lidar com as diferentes situações ou quebras de contrato que podem ocorrer durante a execução do sistema, você estará apenas criando um bloco catch que, no futuro, pode se tornar um verdadeiro monstrinho.</li>
 	<li>Entenda a diferença entre checked e unchecked exceptions.</li>
 	<li>Uma declaração de método que contém uma instrução throws seguida de 293847 tipos de exceção e algo como um "throws Throwable" são a mesma coisa.</li>
 	<li>Se checked exceptions são um problema para você, considere linguagens como Groovy que torna o tratar de exceções uma tarefa opcional</li>
</ul>
Espero com este texto ter clarificado alguns pontos a respeito de um dos aspectos mais interessantes da linguagem Java.

&nbsp;