BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Notícias Java 7: Gestão automática de recursos em Java

Java 7: Gestão automática de recursos em Java

Favoritos

Parte do Projeto Coin é a capacidade de lidar com o Gerenciamento Automático de Recursos ou, simplesmente, ARM (Automatic Resource Management). A proposta é torná-lo mais fácil de se trabalhar com recursos externos, que precisam ser eliminados ou fechados em caso de erros ou conclusão bem sucedida de um bloco de código. Considere a seguinte operação trivial de cópia de arquivo, a partir do tutorial Java Bytestream:

FileInputStream in = null;
FileOutputStream out = null;
try {
  in = new FileInputStream("entrada.txt");
  out = new FileOutputStream("saida.txt");
  int c;
  while ((c = in.read()) != -1)
    out.write(c);
} finally {
  if (in != null)
    in.close();
  if (out != null)
    out.close();
}

Nele, não há apenas códigos repetidos, mas a documentação para o método InputStream.close() diz que este pode lançar uma IOException. (é preferível uma exceção no OutputStream, mas em todo caso, é preciso haver um catch externo ou uma propagation, para que este código possa ser compilado com sucesso).

O escopo do bloco try-catch-finally também requere que as variáveis para o FileInputStream (in) e para o FileOutputStream (out), sejam declaradas fora do bloco propriamente dito. (Se elas estivessem definidas dentro do bloco try, então elas não estariam disponíveis dentro do bloco catch ou finally). Para eliminar este código clichê, e para restringir o escopo dos recursos utilizados dentro do bloco, uma nova adição foi feita ao bloco try na linguagem Java. Uma especificação inicial dos blocos try-with-resources (ou blocos ARM), foi disponibilizada através de uma implementação inicial, que posteriormente fez seu caminho para o build 105 do JDK 7.

A nova interface java.lang.AutoCloseable foi adicionada à API proposta, que define um único método close(), que pode lançar Exception. Este tem sido retro-instalado como um pai do java.io.Closeable, o que significa que todos os InputStream e OutputStream automaticamente tiram vantagem do seu comportamento. Adicionalmente, o FileLock e o ImageInputStream foram também equipados com a interface AutoCloseable.

Isto permite que o exemplo acima possa ser escrito como:

try (
  FileInputStream in = new FileInputStream("entrada.txt");
  FileOutputStream out = new FileOutputStream("saida.txt")
) {
  int c;
  while((c=in.read()) != -1 )
    out.write();
}

No final do bloco try, seja por conclusão normal ou não, ambos os recursos out e in terão o close() chamado automaticamente. Além disso, ao contrário do nosso exemplo original, tanto o out.close() quanto o in.close() são garantidamente executados. (no exemplo original, caso o in.close() lançasse uma exceção, o subsequente out.close() não seria executado). Existem alguns aspectos sutis, que merecem ser citados:

  • No seu estado atual, um ponto-e-vírgula à direita não é permitido depois do recurso final na seção de recursos.
  • O bloco de recursos é separado com () ao invés do mais usual {}, para separá-lo do bloco try existente. Se presente, ele deve conter uma ou mais definições de recursos.
  • Cada definição de recurso é da forma tipo var = expressão; você não pode ter declarações genéricas no bloco de recursos.
  • Os recursos são implicitamente finais; ou seja, eles se comportam como se o modificador final estivesse presente. Qualquer tentativa de atribuição à variável de recursos é um erro em tempo de compilação.
  • Os recursos devem ser um subtipo de AutoCloseable; é um erro em tempo de compilação, se este não for o caso.
  • A ordem de encerramento é a ordem reversa na qual os recursos estão definidos. Em outras palavras, no exemplo refinado, o out.close() é chamado antes do in.close(). Isso permite que streams aninhados sejam construídos e então fechados de fora para dentro, o que faz mais sentido do que em ordem (por exemplo, para liberar buffers antes do stream subjacente ser fechado).
  • Cada bloco pode gerar n+1 exceções, onde n é o número de recursos. Isto pode ocorrer se o método main lançar uma exceção, e cada fechamento de recurso também lançar uma exceção. Nesta situação, a exceção do método será lançada, mas as outras serão adicionadas à lista suprimida de exceções da exceção. Elas podem ser acessadas via getSuppressedExceptions().
  • Rastreamentos de pilhas de exceção podem agora ter o código prefixado com o uso do Suppressed: nestes casos, o formato do Throwable serializado agora é diferente também. (isto pode impactar os clientes Java 6 que estejam invocando serviços remotos Java 7 em tempo de execução, ou vice-versa).
  • javax.swing e java.sql não participam do ARM atualmente; as classes precisam do opt-in através de herança do AutoCloseable, para serem usadas pelo ARM. Caso o JDBC 4.1 faça parte do JDK 7, ele irá suportar o ARM, mas não está claro quando isto irá acontecer.

A capacidade de remover códigos "clichê" do fluxo de trabalho dos desenvolvedores Java, é suscetível a ser um impulso de produtividade dos menores; mas por estar disponível no JDK 7, apenas algum tempo antes do código poder ser escrito é que poderemos tirar vantagem deste fato. Muitas bibliotecas precisarão ser compiladas para rodar contra o Java 6; e qualquer uso do gerenciador automático de recursos (ARM) apenas será aplicável para códigos compilados com o -target 7 (ou similar). Uma vez que o Java 6 está próximo do fim, a utilização do ARM se tornará uma forma automática de se trabalhar.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT