Exceções não tratadas em inicializações de campo

O Java possui alguma syntax para gerenciar exceções que podem ser lançadas ao declarar e inicializar a variável de membro de uma class?

public class MyClass { // Doesn't compile because constructor can throw IOException private static MyFileWriter x = new MyFileWriter("foo.txt"); ... } 

Ou essas inicializações sempre precisam ser movidas para um método no qual possamos declarar throws IOException ou quebrar a boot em um bloco try-catch?

Use um bloco de boot estática

 public class MyClass { private static MyFileWriter x; static { try { x = new MyFileWriter("foo.txt"); } catch (Exception e) { logging_and _stuff_you_might_want_to_terminate_the_app_here_blah(); } // end try-catch } // end static init block ... } 

É uma prática recomendada mover esses tipos de inicializações para methods que podem manipular exceções.

Uma exceção lançada de um iniciador estático pode indicar um problema de design. Realmente você não deveria estar tentando carregar arquivos em estática. Também estático não deve, em geral, ser mutável.

Por exemplo, trabalhando com o JUnit 3.8.1, você pode quase usá-lo em um applet / WebStart, mas ele falhou devido a um inicializador estático fazendo o access ao arquivo. O resto da class envolvida ajustou-se bem ao contexto, é apenas um pouco de estática que não se encheckboxva no contexto e afastou toda a estrutura.

Existem alguns casos legítimos em que uma exceção é lançada. Se for o caso de o ambiente não ter um recurso específico, digamos, porque é um JDK antigo, talvez você queira replace implementações e não há nada fora do comum. Se a class realmente for borked, lance uma exceção não verificada em vez de permitir que uma class corrompida exista.

Dependendo da sua preferência e do problema em questão, há duas maneiras comuns de contornar isso: um inicializador estático explícito e um método estático. (Eu e a maioria das pessoas preferem o primeiro; acredito que Josh Bloch prefere o segundo.)

 private static final Thing thing; static { try { thing = new Thing(); } catch (CheckedThingException exc) { throw new Error(exc); } } 

Ou

 private static final Thing thing = newThing(); private static Thing newThing() { try { return new Thing(); } catch (CheckedThingException exc) { throw new Error(exc); } } 

Nota: as statistics devem ser finais (e geralmente imutáveis). Sendo final, a atribuição única correta é verificada pelo seu compilador amigável. Atribuição definitiva significa que ele pode pegar o tratamento de exceção quebrada – embrulhe e jogue, não imprima / registre. Estranhamente, você não pode usar o nome da class para qualificar a boot com o nome da class no inicializador estático (tenho certeza de que há uma boa razão para isso).

Inicializadores de instância são semelhantes, embora você possa fazer o construtor lançar ou você pode colocar o inicializador dentro do construtor.

Esta construção é ilegal, como você descobriu. Os membros cujos construtores lançam exceções verificadas não podem ser construídos, exceto quando em um contexto que permite o tratamento de exceções, como um construtor, um inicializador de instância ou (para um membro estático) um inicializador estático.

Então, isso seria uma maneira legal de fazer isso:

 public class MyClass { MyFileWriter x; { try { x = new MyFileWriter("foo.txt"); } catch (IOException ex) { ex.printStackTrace(); } } ... } 

Legal, mas feio. Eu preferiria inicializá-lo no construtor e declarar a exceção lá, ou então fazer o usuário chamar um método para inicializá-lo explicitamente. Mas se o usuário deve inicializá-lo, você deve então considerar em qualquer método dependente a possibilidade do object ser inválido.

Se você está escrevendo MyClass como um wrapper para MyFileWriter , eu diria para fazer a boot no construtor. Caso contrário, primeiro questionaria se é necessário ter um gravador aberto durante toda a vida útil do object. Pode ser possível refatorar isso.

Edit: Quando eu estava escrevendo isso, o ” static ” não tinha sido adicionado ao campo. Isso muda bastante as coisas: agora eu gostaria de saber por que você quer ter um gravador aberto para toda a vida útil do carregador de class . Como isso poderia ser fechado?

Este é um sistema de registro caseiro de algum tipo? Nesse caso, recomendo que você dê uma olhada no java.util.logging ou em qualquer um dos muitos frameworks de logging de terceiros.

Eu sugiro um método de fábrica:

  public class MyClass{ private static MyFileWriter fileWriter = MyFileWriter.getFileWriter("foo.txt"); } public class MyFileWriter { /* * Factory method. Opens files, etc etc * @throws IOException. */ public static MyFileWriter getFileWriter(String path) throws IOException{ MyFileWriter writer = new FileWriter(); //stuff that can throw IOException here. return writer; } /*protected constructor*/ protected MyFileWriter(){ //build object here. } } 

Existe outra maneira de tratar a exceção nas inicializações de campo. Vamos considerar seu caso, onde o construtor MyFileWriter lança uma exceção. Considere este código de exemplo para referências.

 import java.io.IOException; public class MyFileWriter { public MyFileWriter(String file) throws IOException{ throw new IOException("welcome to [java-latte.blogpspot.in][1]"); } } 

Se tentarmos inicializar o arquivo assim …..

 public class ExceptionFields{ MyFileWriter f = new MyFileWriter("Hello"); } 

O compilador não nos permitirá inicializar isso. Em vez de fazer a boot no bloco estático, você pode fazer assim

Declare um construtor padrão que lança IOException

 import java.io.IOException; public class ExceptionFields{ MyFileWriter f = new MyFileWriter("Hello"); public ExceptionFields() throws IOException{ } } 

Isto irá inicializar o seu object MyFileWriter.

Se a class tiver apenas um construtor, geralmente movo esses inicializadores para esse construtor.

Se a class tiver mais de um construtor, utilizo um bloco de boot:

 public class MyClass { private static MyFileWriter x; // initialization block start here { try { x = new MyFileWriter(".."); } catch(Exception e) { // exception handling goes here } } public MyClass() { // ctor #1 } public MyClass(int n) { // ctor #2 } } 

O bom de um init. block é que ele é “injetado” no começo de todo construtor. Assim, você não precisa duplicar os inicializadores.