2009-06-22 17 views
21

¿Java tiene alguna sintaxis para gestionar las excepciones que se pueden lanzar al declarar e inicializar la variable miembro de una clase?Excepciones no controladas en inicializaciones de campo

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

¿O esas inicializaciones siempre tienen que ser trasladado a un método en el que podemos declarar throws IOException o envuelva la inicialización en un bloque try-catch?

+0

La respuesta de akf es correcta, pero si odias declarar nuevos métodos, también puedes usar bloques estáticos para inicializar tus variables estáticas; puedes tener try/catch dentro de bloques estáticos. –

+0

Para que otros no lean mi último comentario como una aprobación del enfoque de bloqueo estático: prefiero crear nuevos métodos. Solo sugerí una alternativa para aquellos que no lo hacen. :-) –

+0

Lo siento, olvidé marcar el campo como estático. ¿La recomendación todavía se aplica? – kpozin

Respuesta

19

Use un bloque de inicialización 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 
    ... 
} 
+2

también tiene la opción de lanzar una RuntimeException si desea propagar después de su manejo. – akf

+0

¿No es esto un problema si tiene más de una clase con un miembro de MyFileWriter? Tendría que reescribir su bloque estático en cada clase. – Tom

+0

Como es un miembro de la clase, ¿no viene eso solo con el territorio? – MattC

3

es una práctica recomendada mover ese tipo de inicializaciones a métodos que pueden manejar excepciones.

+0

de acuerdo. Implementé un método Factory en mi respuesta. – Tom

2

Esta construcción es ilegal, como has descubierto. Los miembros cuyos constructores lanzan excepciones comprobadas no pueden construirse excepto en un contexto que permite el manejo de excepciones, como un constructor, un inicializador de instancia o (para un miembro estático) un inicializador estático.

lo que esta sería una forma legal de hacerlo:

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

legal, sino más bien feo. Preferiría inicializarlo en el constructor y declarar allí la excepción, o bien hacer que el usuario llame a un método para inicializarlo explícitamente. Pero si el usuario debe inicializarlo, debe contabilizar en cualquier método dependiente la posibilidad de que el objeto no sea válido.

Si está escribiendo MyClass como un contenedor para MyFileWriter, yo diría que haga la inicialización en el constructor. De lo contrario, primero me pregunto si es necesario tener un escritor abierto durante toda la vida del objeto. Es posible refactorizar esto.

Edit: Cuando estaba escribiendo esto, el "static" no se había agregado al campo. Esto cambia bastante las cosas: ahora me gustaría saber por qué querría tener un escritor abierto para el classloader toda la vida. ¿Cómo podría ser cerrado alguna vez?

¿Es este un sistema de registro de cosecha propia de algún tipo? Si es así, te animo a echar un vistazo al java.util.logging o a cualquiera de los muchos frameworks de registro de terceros que existen.

0

Si la clase tiene solo un constructor, generalmente muevo tales inicializadores a ese constructor.

Si la clase tiene más de un constructor, que utilice un bloque de inicialización:

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 
    } 
} 

Lo bueno de un INIT. bloque es que se "inyecta" al comienzo de cada constructor. Por lo tanto, no es necesario duplicar los inicializadores.

0

que sugieren un 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. 

    } 
    } 
+2

Esto no soluciona el problema, no tiene manejo de excepciones en la llamada a getFileWriter(), y para agregarlo deberá colocar un bloque estático. – Robin

3

Una excepción lanzada desde un intialiser estática puede indicar un problema de diseño. Realmente no deberías intentar cargar archivos en statics. También estático no debe, en general, ser mutable.

Por ejemplo, trabajando con JUnit 3.8.1 casi podría usarlo desde un applet/WebStart, pero falló debido a que un inicializador estático realizaba acceso a archivos. El resto de la clase involucrada se ajustó al contexto, es solo esta parte de la estática lo que no encajó en el contexto y destruyó todo el marco.

Existen casos legítimos en los que se produce una excepción. Si se trata de un caso en el que el entorno no tiene una característica en particular, por ejemplo, porque es un JDK antiguo, entonces es posible que desee sustituir las implementaciones, y no hay nada fuera de lo común. Si la clase realmente tiene borked, ejecute una excepción sin marcar en lugar de permitir que exista una clase rota.

Dependiendo de su preferencia y el problema en cuestión, hay dos formas comunes de evitarlo: un inicializador estático explícito y un método estático. (I, y creo que la mayoría de la gente, prefiero la primera, creo Josh Bloch prefiere este último.)

private static final Thing thing; 

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

O

private static final Thing thing = newThing(); 

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

Nota: la estática deben ser definitiva (y generalmente inmutables). Al ser final, la correcta asignación individual es verificada por su amigable compilador. La asignación definitiva significa que puede detectar el manejo de excepciones interrumpidas: envolver y arrojar, no imprimir/registrar. Extrañamente, no se puede usar el nombre de la clase para calificar la inicialización con el nombre de la clase en el inicializador estático (estoy seguro de que hay una buena razón para esto).

Los inicializadores de instancia son similares, aunque puede hacer que el constructor lance o puede colocar el inicializador dentro del constructor.

0

Hay otra manera de manejar la excepción en las inicializaciones de campo. Consideremos su caso, donde el constructor MyFileWriter lanza una excepción. Considere este código de muestra para referencias.

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

Si tratamos de inicializar el archivo como esto .....

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

} 

compilador no nos permitirá iniciar este. En vez de hacer la inicialización en el bloque estático, se puede hacer como esto

declarar un constructor por defecto que lanza IOException

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

    }  
} 

Esto iniciará el objeto MyFileWriter.

Cuestiones relacionadas