Post traduzido e adaptado do Forum da Oracle.
Texto original de Manfred Riem.
Saiba como aproveitar melhor o tratamento de exceções, uma de muitas mudanças úteis encontradas no Project Coin, em Java SE 7.
O Project Coin é um dos projetos que foram desenvolvidos para a implementação do Java 7. A ideia do projeto era adicionar pequenas mudanças na linguagem, sem a necessidade de depender de outros projetos do java 7, e sem forçar os desenvolvedores a aderirem à nova sintaxe.
Algumas das melhorias implementadas pelo Project Coin são:
- String no uso de Switch
- Melhorias na escrita de literais do tipo Integral (byte, short, int e long)
- Multiplos Catch em Exception, tratados aqui
- Definição de tipo melhorada para criação de instâncias genéricas (“diamond”)
- Try-com-recursos, tratados aqui
- Invocação simplificada do método varargs
Exceções com Multi-Catch
Exceções com Multi-Catch foram adicionadas ao Java SE 7 para mais fácil e mais conciso a captura de exceções. Veja abaixo como migrar o seu código de manipulação de exceções de um código pré-Java SE 7 para um código Java SE 7.
Exemplo 1:
public class ExampleExceptionHandling { public static void main( String[] args ) { try { URL url = new URL("http://www.yoursimpledate.server/"); BufferedReader reader = new BufferedReader(newInputStreamReader(url.openStream())); String line = reader.readLine(); SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY"); Date date = format.parse(line); } catch(ParseException exception) { // Capturando problemas de conversão de URL. } catch(IOException exception) { // Capturando problemas de I/O. } catch(ParseException exception) { // Capturando problemas de conversão de datas. } } }
No passado, se você quisesse ter a mesma lógica para tratar dois ou três casos de Exception, como por exemplo ParseException e IOException, você tinha que copiar e colar o mesmo código em ambos os blocos Catch. Um programador inexperiente ou preguiçoso poderia pensar que estaria tudo bem fazer o seguinte:
Exemplo 2:
public class ExampleExceptionHandlingLazy { public static void main( String[] args ) { try { URL url = new URL("http://www.yoursimpledate.server/"); BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); String line = reader.readLine(); SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY"); Date date = format.parse(line); } catch(Exception exception) { // Sou um programador inexperiente ou preguiçoso. } } }
O maior problema com o código do Exemplo 2 é que ele poderia apresentar efeitos colaterias indesejados. Qualquer código no bloco try pode lançar uma exceção que seria engolida com uma cláusula catch (Exception). Se uma exceção que não é um ParseException ou IOException for lançada (por exemplo, um SecurityException), o código vai pegá-lo, mas o usuário pode não ficar ciente do que realmente aconteceu. Absorver uma exceção como esta, faz com que o código fique difícil de debugar.
A fim de facilitar o trabalho do programador, Java SE 7 agora inclui uma declaração multi-catch. Isto permite que o programador combine uma clausula catch em um simples bloco de código sem a necessidade de usar uma perigosa cláusula catch-all ou copiar o mesmo código entre vários blocos.
Exemplo 3:
public class ExampleExceptionHandlingNew { public static void main( String[] args ) { try { URL url = new URL("http://www.yoursimpledate.server/"); BufferedReader reader = new BufferedReader( new InputStreamReader(url.openStream())); String line = reader.readLine(); SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY"); Date date = format.parse(line); } catch(ParseException | IOException exception) { // Trate seus problemas aqui. } } }
O exemplo 3 mostra como combinar adequadamente as duas declarações em um bloco catch. Observe a sintaxe para a cláusula catch (ParseException | IOException). Esta cláusula catch vai pegar tanto ParseException e IOException.
Então, se você quer agora compartilhar o mesmo código de captura de exceção para duas distintas exceções, você pode usar a sintaxe “Pipe”: (ExceptionType | … | ExceptionType variavle).
Relançar Exceptions
Ao fazer o tratamento de exceções, há momentos em que você quer re-lançar uma exceção que esteja tratando. Um programador inexperiente pode pensar em fazer o seguinte código:
Exemplo 4:
public class ExampleExceptionRethrowInvalid { public static void demoRethrow()throws IOException { try { // Forçando uma IOException aqui como um exemplo, // normalmente algum código começa aqui. throw new IOException(“Error”); } catch(Exception exception) { /* * faça alguma tratativa e então relance a exception. */ throw exception; } } public static void main( String[] args ) { try { demoRethrow(); } catch(IOException exception) { System.err.println(exception.getMessage()); } } }
Mas o compilador não irá compilar o código no Exemplo 4. Exemplo 5 mostra uma maneira de lidar com a exceção e, em seguida, “relançar” ele:
Exemplo 5:
public class ExampleExceptionRethrowOld { public static demoRethrow() { try { throw new IOException("Error"); } catch(IOException exception) { /* * Do some handling and then rethrow. */ throw new RuntimeException(exception); } } public static void main( String[] args ) { try { demoRethrow(); } catch(RuntimeException exception) { System.err.println(exception.getCause().getMessage()); } } }
O problema com o Exemplo 5 é que não está realmente relançando a excepção original. O código está envolvendo a exceção com uma outra exceção, o que significa que o código seguinte precisa estar ciente de que a exceção foi envolvida. Assim, para tornar possível a captura efetiva da exceção original, uma mudança foi necessária em Java SE (mostrado no exemplo 6).
Exemplo 6:
public class ExampleExceptionRethrowSE7 { public static demoRethrow() throws IOException { try { throw new IOException("Error"); } catch(Exception exception) { /* * Do some handling and then rethrow. */ throw exception; } } public static void main( String[] args ) { try { demoRethrow(); } catch(IOException exception) { System.err.println(exception.getMessage()); } } }
Try-Com-Recursos
Você deve ter notado que há um problema com o Exemplo 1 (que é por isso que você nunca deve usar o código do exemplo, em um ambiente de produção sem saber o que ele faz). O problema é que não existe nenhuma limpeza dos recursos utilizados no interior do bloco de teste. Exemplo 7 é uma versão atualizada que descreve como, antes do Java SE 7, um programador resolve este problema.
Exemplo 7:
public class ExampleTryResources { public static void main(String[] args) { BufferedReader reader = null; try { URL url = new URL("http://www.yoursimpledate.server/"); reader = new BufferedReader(new InputStreamReader(url.openStream())); String line = reader.readLine(); SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY"); Date date = format.parse(line); } catch (MalformedURLException exception) { // Capturando problemas de conversão de URL. } catch (IOException exception) { // Capturando problemas de I/O. } catch (ParseException exception) { // Capturando problemas de conversão de datas. } finally { if (reader != null) { try { reader.close(); } catch (IOException ex) { ex.printStackTrace(); } } } } }
Observe que tivemos que adicionar um bloco finally que fecha o BufferedReader caso ele tenha sido inicializado. Além disso, note que agora temos a variável leitora fora do bloco try. Isso é um pouco mais de código do que necessitamos se a única coisa que você precisa fazer é fechar o leitor se uma exceção I/O acontecer.
No Java SE 7, isto pode ser feito de uma forma mais concisa e limpa, com omostrado no exemplo 8. A nova sintaxe permite que você declare recursos que fazem parte do bloco try. O que isto significa é que você define os recursos antes do tempo e o tempo de execução fecha automaticamente os recursos (caso já não estejam fechados), após a execução do bloco try.
Exemplo 8:
public static void main (String [] args) { tentar (BufferedReader reader = new BufferedReader ( new InputStreamReader ( nova URL ("http://www.yoursimpledate.server/"). OpenStream ()))) { Seqüência de linha = reader.readLine (); SimpleDateFormat formato = new SimpleDateFormat ("MM / DD / AA"); Data = Data format.parse (linha); } Catch (ParseException | exceção IOException) { // Lidar com os problemas de I / O. } }
Note que no exemplo 8, a abertura atual acontece na declaração do try (…). Esteja ciente de que este recurso só funciona em classes que implementam a interface AutoCloseable.
Conclusão
A mudanças na manipulação de exceções do Java SE 7 permite que você não apenas programe de forma mais concisa, como demonstrado nos exemplos de multi-catch, mas eles também permitem que você manipule parcialmente uma exceção e, em seguida, relance, como mostrado nos exemplos. Java SE 7 vai também facilitar a criação de códigos para tratativas de erros mais limpos, assim como vimos nos exemplos de try-com-recursos. Estes recursos, juntamente com outros oferecidos pelo Project Coin, permitem que os desenvolvedores Java sejam mais produtivos e escrevam códigos mais eficientes.