# Tratamento de exceções em Java: o que há de especial?

As exceções em Java são um pouco diferentes se comparadas com as de outras linguagens de programação. Eu não sei se esse é o seu caso, mas, quando comecei a estudar Java, eu sentia essa diferença enquanto programava e não sabia explicar qual era o motivo.

Por exemplo, por que algumas exceções produzem erros de compilação quando não são tratadas enquanto outras não? E, em relação a esses erros produzidos, por que eles são "magicamente" resolvidos ao especificar os tipos das exceções após a palavra-chave `throws`, na assinatura do método pelo qual são lançadas?

No final das contas, a resposta é simples (ou nem tanto, conforme explico na última seção). Basta entender como as exceções em Java foram projetadas e a razão pela qual elas foram organizadas da forma que foram. É isso que eu tentarei te explicar até o fim deste artigo.

## Introdução às exceções

Por baixo do pano, todo método define uma espécie de contrato com o código que o consome. Nele, especifica-se detalhes como a forma de utilizá-lo (ou seja, seus parâmetros) e o comportamento esperado ao chamá-lo (produz um valor de retorno ou [efeitos colaterais](https://en.wikipedia.org/wiki/Side_effect_(computer_science))).

No entanto, há situações em que um método não é capaz de seguir o seu fluxo padrão, geralmente em decorrência de fatores externos, como o ambiente em que o programa está sendo executado.

Nesse contexto, um exemplo comumente utilizado é o de um método que recebe do usuário o nome de um arquivo e tenta abri-lo: caso o nome informado não exista, o arquivo não pode ser aberto, e o método é impedido de prosseguir da forma esperada.

É esse aspecto que as exceções representam. Por meio delas, é possível sinalizar a ocorrência de **situações excepcionais** que podem acontecer ao invocar um método, e essas situações podem ser detectadas e "tratadas" pelo desenvolvedor por meio de um mecanismo adequado — os famosos **blocos try-catch**.

```java
// código omitido
public void doSomething() {
    try {
        doAnotherThing(); // potencialmente lança ExampleException
    } catch (ExampleException e) {
        // tratamento da exceção
    }
}
// código omitido
```

Tradicionalmente, o tratamento de uma exceção não é obrigatório e não precisa ser feito pelo método que a lançou (nem mesmo por outros métodos que o tenham invocado!), o que é uma vantagem até certo ponto, pois possibilita que as exceções "borbulhem" (*bubble*) de baixo para cima na [*call stack*](https://www.baeldung.com/cs/call-stack) até o local mais adequado para o tratamento. A figura 1 ilustra esse comportamento.

<figure>
	<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686354060784/ead7c1a4-7a2e-4fec-b0a0-c72cb0ca4943.png" alt="Pilha de chamadas (call stack) composta por métodos nomeados de A a D dispostos em ordem crescente de cima para baixo. O método A invoca o método B, que invoca o método C, e este, por fim, invoca o método D. O método D lança uma exceção, que sobe pela pilha de chamadas como se fosse bolhas de ar subindo dentro d'água. Os métodos B e C não sabem ou não querem tratar a exceção, deixando-a passar, ao passo que o método A sabe o que fazer, capturando-a e estourando a bolha.">
	<figcaption>Figura 1 - As exceções "borbulham" na call stack até que um método as capture.</figcaption>
</figure>

## Checked vs. Unchecked Exceptions

Ocorre que a linguagem Java considera as exceções lançadas por um método uma parte do seu próprio contrato, e sua sintaxe obriga que elas sejam informadas a qualquer código que o invoque. Em outras palavras, as exceções precisam [constar na assinatura do método](https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html), da mesma forma que seus parâmetros e valor de retorno.

Esse requisito é justificado pela noção de que o desenvolvedor deve ser capaz de **antecipar** e contornar exceções, seja exibindo uma mensagem de erro ao usuário, seja buscando uma rota alternativa para a ação solicitada. É por esse motivo que especificamos as exceções após a palavra-chave `throws`.

```java
// código omitido
public void doAnotherThing() throws ExampleException {
    // código omitido
    if (condition) {
        /*
            ExampleException representa uma situação que é prevista ao
            invocar o método doAnotherThing, mas que ocorre apenas em
            condições excepcionais (por exemplo, se "condition"
            for verdadeiro). Espera-se que o código consumidor
            antecipe essa situação e a trate de maneira adequada.
        */
        throw new ExampleException()
    }
    // código omitido
}
// código omitido
```

Dessa forma, sempre que lidamos com um código que possivelmente lança exceções, temos duas opções: ou as tratamos imediatamente (com try-catch) ou as tornamos parte do próprio contrato do método no qual trabalhamos (com `throws` em sua assinatura), esperando que um outro método faça o tratamento. Esse comportamento é denominado [Catch or Specify Requirement](https://docs.oracle.com/javase/tutorial/essential/exceptions/catchOrDeclare.html).

<figure>
	<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686354121131/b2673326-ed7a-419f-977f-69503fefe36f.png" alt="Pilha de chamadas (call stack) idêntica à da figura 1, contendo métodos nomeados de A a D. Entre o método D, que lança uma exceção, e o método A, que a captura, todos os outros métodos explicitam para o próximo que eles lançam a mesma exceção lançada por D, por meio da cláusula throws.">
	<figcaption>Figura 2 - Todos os métodos que trabalham com uma exceção, mas não a tratam, precisam torná-la parte do seu próprio contrato com <span lang="en">"throws"</span>.</figcaption>
</figure>

Para garantir que o desenvolvedor tome conhecimento sobre as exceções possivelmente lançadas por um método, o compilador Java acusa um erro de compilação caso uma exceção não tenha sido tratada (ou, ao menos, postergada com `throws`). Sendo assim, as exceções em Java são conhecidas como *checked exceptions* (exceções verificadas).

No entanto, algumas exceções são naturalmente [internas à aplicação](https://docs.oracle.com/javase/tutorial/essential/exceptions/catchOrDeclare.html) (não devem ser expostas pelo contrato) e não podem ser contornadas de forma satisfatória, pois geralmente decorrem de [defeitos de programação](https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html) (como uma tentativa de acessar campos em um valor nulo ou realizar uma divisão por zero). Essas exceções são conhecidas como *unchecked exceptions* (exceções não verificadas).

<details data-node-type="hn-details-summary"><summary>Unchecked exceptions vs. Runtime exceptions</summary><div data-type="detailsContent">Na realidade, o termo <strong>runtime exception</strong> representaria com maior precisão o tipo de exceções referidas como <em>unchecked exceptions</em> neste artigo, pois <em>unchecked exceptions</em> também englobam o conceito de <strong>erros, </strong>que são causados por <a target="_blank" rel="noopener noreferrer nofollow" href="https://docs.oracle.com/javase/tutorial/essential/exceptions/catchOrDeclare.html" style="pointer-events: none">condições externas às regras de negócio</a>, como falhas de <em>hardware </em>ou do sistema. Porém, optei por utilizar esses termos como sinônimos para fins de simplificação.</div></details>

Como o nome sugere, as *unchecked exceptions* não são verificadas pelo compilador e, portanto, não é obrigatório tratá-las, o que as torna semelhantes às presentes em outras linguagens de programação. Isso ocorre pois, embora seja possível capturá-las, o ideal seria detectar e corrigir os bugs que as causam o mais rápido possível.

Na prática, **todas** as exceções em Java são derivadas da classe `Exception` e são verificadas por padrão. Porém, é possível criar exceções não verificadas a partir da classe `RuntimeException` (que também é uma `Exception`!). A figura 3 ilustra a hierarquia de exceções em Java.

<figure>
	<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686354176087/8420a411-f26d-40ad-85e6-6cd0be004674.png" alt="Todas as exceções derivam da classe Exception, incluindo IOException, SQLException e RuntimeException, entre outras. Todas as exceções são verificadas pelo compilador, exceto por RuntimeException e suas derivadas, como NullPointerException e ArithmeticException, que não são verificadas.">
	<figcaption>Figura 3 - Hierarquia de exceções em Java. As classes Throwable e Error foram omitidas para fins de simplificação.</figcaption>
</figure>

## **Controvérsia**

Os princípios básicos sobre quando utilizar cada tipo de exceção foram abordados até agora, mas há uma controvérsia entre os desenvolvedores Java, que questionam as vantagens e desvantagens de *checked* e *unchecked exceptions*.

Em meio a vários outros argumentos, alguns afirmam que *checked exceptions* dificultam a [manutenção da base de código](https://www.artima.com/articles/the-trouble-with-checked-exceptions) em projetos de larga escala, enquanto outros argumentam que elas são essenciais para garantir que desenvolvedores sejam capazes de [antecipar situações excepcionais](https://stackoverflow.com/a/614494) ao consumir uma API.

Não entrarei em mais detalhes, pois trata-se de um assunto complexo e que foge do escopo deste artigo, porém, disponibilizarei algumas leituras que encontrei sobre o assunto juntamente às referências.

De modo geral, me parece ser um consenso que as *checked exceptions* têm, de fato, a sua utilidade, desde que não abusemos do seu uso, sob o risco de dificultar a manutenção do código. Além disso, convém nos lembrarmos de sempre manter a consistência com as convenções já utilizadas dentro de um projeto.

## Conclusão

As exceções em Java derivam da classe `Exception` e são consideradas parte do contrato do método pelo qual são lançadas, devendo ser especificadas em sua assinatura por meio da cláusula `throws`. Nesse mesmo sentido, essas exceções são chamadas de *checked exceptions*, pois têm seu tratamento garantido pelo compilador, que acusa um erro caso não sejam tratadas.

No entanto, há exceções especiais — chamadas de *unchecked exceptions* — que se originam da classe `RuntimeException` (também derivada de `Exception`) e não são verificadas pelo compilador, pois são internas ao escopo de um método e, por esse motivo, não são expostas pelo seu contrato.

## Referências

1. [Side effect (computer science) (wikipedia.org)](https://en.wikipedia.org/wiki/Side_effect_(computer_science))
    
2. [The Call Stack | Baeldung on Computer Science (baeldung.com)](https://www.baeldung.com/cs/call-stack)
    
3. [Unchecked Exceptions — The Controversy (The Java™ Tutorials &gt; Essential Java Classes &gt; Exceptions) (](https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html)[oracle.com](http://oracle.com)[)](https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html)
    
4. [The Catch or Specify Requirement (The Java™ Tutorials &gt; Essential Java Classes &gt; Exceptions) (](https://docs.oracle.com/javase/tutorial/essential/exceptions/catchOrDeclare.html)[oracle.com](http://oracle.com)[)](https://docs.oracle.com/javase/tutorial/essential/exceptions/catchOrDeclare.html)
    
5. [artima - The Trouble with Checked Exceptions (www.artima.com)](https://www.artima.com/articles/the-trouble-with-checked-exceptions)
    
6. [java - The case against checked exceptions - Stack Overflow (stackoverflow.com)](https://stackoverflow.com/questions/613954/the-case-against-checked-exceptions/614494#614494)
    
7. [What Is an Exception? (The Java™ Tutorials &gt; Essential Java Classes &gt; Exceptions) (](https://docs.oracle.com/javase/tutorial/essential/exceptions/definition.html)[oracle.com](http://oracle.com)[)](https://docs.oracle.com/javase/tutorial/essential/exceptions/definition.html)
    
8. [Google Answers: The origin of checked exceptions (](https://web.archive.org/web/20220409162401/http://answers.google.com/answers/threadview?id=26101)[archive.org](http://archive.org)[)](https://web.archive.org/web/20220409162401/http://answers.google.com/answers/threadview?id=26101)
    

### Discussões sobre o uso de checked x uncheckd exceptions

* [artima - The Trouble with Checked Exceptions (www.artima.com)](https://www.artima.com/articles/the-trouble-with-checked-exceptions)
    
* [java - The case against checked exceptions - Stack Overflow (stackoverflow.com)](https://stackoverflow.com/questions/613954/the-case-against-checked-exceptions/614494#614494)
    
* [Bruce Eckel's MindView, Inc: Does Java need Checked Exceptions? (](https://web.archive.org/web/20180301074914/http://www.mindview.net/Etc/Discussions/CheckedExceptions)[archive.org](http://archive.org)[)](https://web.archive.org/web/20180301074914/http://www.mindview.net/Etc/Discussions/CheckedExceptions)
    
* [Java's checked exceptions were a mistake (and here's what I would like to do about it) (](https://web.archive.org/web/20171109034001/http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html)[archive.org](http://archive.org)[)](https://web.archive.org/web/20171109034001/http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html)
    
* [java - When to choose checked and unchecked exceptions - Stack Overflow (stackoverflow.com)](https://stackoverflow.com/questions/27578/when-to-choose-checked-and-unchecked-exceptions)
    
* [In Java, when should I create a checked exception, and when should it be a runtime exception? - Stack Overflow (stackoverflow.com)](https://stackoverflow.com/questions/499437/in-java-when-should-i-create-a-checked-exception-and-when-should-it-be-a-runti)
    

## Atribuições

* Fotografia da capa por [Gabrielle Henderson](https://unsplash.com/@gabriellefaithhenderson) em [Unsplash](https://unsplash.com/)
