views:

178

answers:

6

Does Java have any syntax for managing exceptions that might be thrown when declaring and initializing a class's member variable?

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

Or do such initializations always have to be moved into a method where we can declare throws IOException or wrap the initialization in a try-catch block?

+2  A: 

it is best practice to move those sorts of initializations to methods that can property handle exceptions.

akf
agreed. I implemented a Factory method in my answer.
Tom
+7  A: 

Use a static initialization block

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
  ...
}
MattC
you also have the option to throw a RuntimeException if you want to propagate after you do your handling.
akf
Isn't this a problem if you have more than one class witha MyFileWriter member? You would have to rewrite your static block in every class.
Tom
Since it's a class member, doesn't that just come with the territory?
MattC
If the MyFileWriter has to be initialized for the class to function properly, then a RuntimeException should be thrown which will prevent the class from being loaded. This would effectively render any code trying to use this class doomed to fail, which is probably the desired behaviour if it is crucial to the running of your application. Logging without doing this will potentially make every call to MyClass methods fail instead.
Robin
+2  A: 

This construction is illegal, as you have discovered. Members whose constructors throw checked exceptions cannot be constructed except when in a context that allows exception handling, such as a constructor, an instance initializer, or (for a static member) a static initializer.

So this would be a legal way to do it:

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

Legal, but rather ugly. I would prefer either initializing it in the constructor and declaring the exception there, or else making the user call a method to explicitly initialize it. But if the user must initialize it, you must then account in any dependent methods for the possibility of the object's being invalid.

If you are writing MyClass as a wrapper for MyFileWriter, I would say to do the initialization in the constructor. Otherwise, I would first question whether it is necessary to have a writer open during the object's entire lifetime. It may be possible to refactor this away.

Edit: When I was writing this, the "static" hadn't been added to the field. This changes things quite a bit: I now would like to know why on earth you want to have a writer open for the classloader's entire lifetime. How could it ever be closed?

Is this a home-grown logging system of some sort? If so, I'd encourage you to have a look at java.util.logging or any of the many fine third-party logging frameworks around.

Michael Myers
A: 

If the class has only one constructor I usually move such initializers into that constructor. If the class has more than one constructor I use an initialization block:

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
   }

}

The nice thing about an init. blocks is that it is "injected" into the beginning of every constructor. Thus, you don't need to duplicate the initializes.

Itay
A: 

I suggest a factory method:

  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.

    } 
  }
Tom
This doesn't fix the problem, you have no exception handling in the call to the getFileWriter(), and to add it you will need to put in a static block.
Robin
+1  A: 

An exception thrown from a static intialiser may indicate a design problem. Really you shouldn't be attempting to load files into statics. Also static should not, in general, be mutable.

For instance, working back with JUnit 3.8.1 you could almost use it from an applet/WebStart, but it failed due to one static initialiser doing file access. The rest of the class involved was fitted the context fine, it's just this bit of static which did not fit the context and blew the whole framework away.

There are, some legitimate cases where an exception is thrown. If it's a case that the environment doesn't have a particular feature, say, because it's an old JDK, then you might want to substitute implementations, and there is nothing out of the ordinary. If the class really is borked, throw an unchecked exception rather than allowing a broken class to exist.

Depending on your preference and the problem at hand, there's two common ways to go around it: an explicit static initialiser and a static method. (I, and I think most people, prefer the former; I believe Josh Bloch prefers the latter.)

private static final Thing thing;

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

Or

private static final Thing thing = newThing();

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

Note: statics should be final (and generally immutable). Being final, correct single assignment is checked by your friendly compiler. Definite assignment means that it may catch broken exception handling - wrap and throw, do not print/log. Strangely, you can't user the class name to qualify the initialisation with the class name in the static initialiser (I'm sure there is a good reason for this).

Instance initialisers are similar, although you can make the constructor throw or you can put the initialiser within the constructor.

Tom Hawtin - tackline