views:

108

answers:

3

My objective is to have a private static Properties object in my class, to act as defaults when creating other Properties objects needed by my application. The current implementation looks like this:

public class MyClass {
    private static Properties DEFAULT_PROPERTIES = new Properties();

    static {
        try {
           DEFAULT_PROPERTIES.load(
               MyClass.class.getResourceAsStream("myclass.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
 }

Looking at it, it works, but it doesn't feel right.

How would you do it?

A: 

Seems acceptable to me; load in the static initialiser, it gets called only when the class is referenced, and is only called once. I like it. The only thing I'd do is make it final.

Well, aside from the exception. I'd try and avoid that somehow (I have in the back of my mind that you should avoid exceptions in those types of initialisers, but I could be wrong on that).

Noon Silk
+4  A: 

Instead of a generic RuntimeException, I would throw an ExceptionInInitializerError, which is ment for exacctly this purpose. From the API documentation: "Signals that an unexpected exception has occurred in a static initializer."

jarnbjo
Oh, I didn't know that. Thanks for the hint!
Igor
@jarnbjo I think it would be helpful if you provide a link to the API doc (of ExceptionInitializer) too:http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ExceptionInInitializerError.html
sateesh
@sateesh: If a Java developer is not able to find a class in the API documentation without a link in my answer, it is time that he learns how to do it.
jarnbjo
+2  A: 

There are basically two ways. First way is using the static block as you have shown (but then with an ExceptionInInitializerError instead of the RuntimeException). Second way is using a static method which you call immediately on declaration:

private static Properties DEFAULT_PROPERTIES = getDefaultProperties();

private static Properties getDefaultProperties() {
    Properties properties = new Properties();
    try {
        properties.load(MyClass.class.getResourceAsStream("myclass.properties"));
    } catch (IOException e) {
        throw new ConfigurationException("Cannot load properties file", e);
    }
    return properties;
}

The ConfigurationException can just be your custom class extending RuntimeException.

I personally prefer the static block because it doesn't make sense having a method which is executed only once ever in its life. But if you refactor the method so that it takes a filename and can be reused globally, then that would be more preferred.

private static Properties DEFAULT_PROPERTIES = SomeUtil.getProperties("myclass.properties");

// Put this in a SomeUtil class.
public static Properties getProperties(String filename) {
    Properties properties = new Properties();
    try {
        properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(filename));
    } catch (IOException e) {
        throw new ConfigurationException("Cannot load " + filename, e);
    }
    return properties;
}
BalusC
This actually fits in my problem, because I'll create more classes with the same pattern, so it makes sense to create an abstract class and fill it with the `getProperties()` method, so it can be reused in all my subclasses. Very informative, thanks!
Igor
Note that I changed the way to load the properties file by using `Thread#getContextClassLoader()`. Don't forget to alter this in your code as well.
BalusC
Will this load the resource in the same package the class calling `getProperties()`?
Igor
I am not sure if I understand you. Either way is just package-independent. You can specify package in the filename. The improved way is just class-independent and makes use of the classloader which already loaded the calling class. This way you ensure that the resource never returns null, which may happen if the calling class isn't loaded by the thread's classloader.
BalusC
I was referring that `org.example.MyClass.class.getResourceAsStream("myclass.properties")` will open the resource `ROOT/org/example/myclass.properties`, while using the `ClassLoader` it will look for resources in the `ROOT/` directory. Am I wrong?
Igor
Oh this way. Yes, just specify the package in the filename.
BalusC