开发者

Can I call methods in constructor in Java?

I have situation, where I want to read configuration file only one time, when class is instantiated.

Suppose I have a method named readConfig(), that reads configuration and puts it into a Map object. When the program is required to use configuration value it reads object with it's define key. I want to know that constructor calls only once it's life cycle. Can I put my method readConfig() into constructor, which would give me benefit of开发者_运维问答 one time calling or is there another mechanism to do that?


You can: this is what constructors are for. Also you make it clear that the object is never constructed in an unknown state (without configuration loaded).

You shouldn't: calling instance method in constructor is dangerous because the object is not yet fully initialized (this applies mainly to methods than can be overridden). Also complex processing in constructor is known to have a negative impact on testability.


Better design would be

public static YourObject getMyObject(File configFile){
    //process and create an object configure it and return it
}
  • Factory design pattern


Can I put my method readConfig() into constructor?

Invoking a not overridable method in a constructor is an acceptable approach.
While if the method is only used by the constructor you may wonder if extracting it into a method (even private) is really required.

If you choose to extract some logic done by the constructor into a method, as for any method you have to choose a access modifier that fits to the method requirement but in this specific case it matters further as protecting the method against the overriding of the method has to be done at risk of making the super class constructor inconsistent.

So it should be private if it is used only by the constructor(s) (and instance methods) of the class.
Otherwise it should be both package-private and final if the method is reused inside the package or in the subclasses.

which would give me benefit of one time calling or is there another mechanism to do that ?

You don't have any benefit or drawback to use this way.
I don't encourage to perform much logic in constructors but in some cases it may make sense to init multiple things in a constructor.
For example the copy constructor may perform a lot of things.
Multiple JDK classes illustrate that.
Take for example the HashMap copy constructor that constructs a new HashMap with the same mappings as the specified Map parameter :

public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        if (table == null) { // pre-size
            float ft = ((float)s / loadFactor) + 1.0F;
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            if (t > threshold)
                threshold = tableSizeFor(t);
        }
        else if (s > threshold)
            resize();
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

Extracting the logic of the map populating in putMapEntries() is a good thing because it allows :

  • reusing the method in other contexts. For example clone() and putAll() use it too
  • (minor but interesting) giving a meaningful name that conveys the performed logic


The constructor is called only once, so you can safely do what you want, however the disadvantage of calling methods from within the constructor, rather than directly, is that you don't get direct feedback if the method fails. This gets more difficult the more methods you call.

One solution is to provide methods that you can call to query the 'health' of the object once it's been constructed. For example the method isConfigOK() can be used to see if the config read operation was OK.

Another solution is to throw exceptions in the constructor upon failure, but it really depends on how 'fatal' these failures are.

class A
{
    Map <String,String> config = null;
    public A()
    {
        readConfig();
    }

    protected boolean readConfig()
    {
        ...
    }

    public boolean isConfigOK()
    {
        // Check config here
        return true;
    }
};


You can. But by placing this in the constructor you are making your object hard to test.

Instead you should:

  • provide the configuration with a setter
  • have a separate init() method

Dependency injection frameworks give you these options.

public class ConfigurableObject {
   private Map<String, String> configuration;
   public ConfigurableObject() {

   }

   public void setConfiguration(..) {
       //...simply set the configuration
   }
}

An example of the 2nd option (best used when the object is managed by a container):

public class ConfigurableObject {
   private File configFile;
   private Map<String, String> configuration;
   public ConfigurableObject(File configFile) {
       this.configFile = configFile;
   }

   public void init() {
       this.configuration = parseConfig(); // implement
   }
}

This, of course, can be written by just having the constructor

public ConfigurableObject(File configfile) {
    this.configuration = parseConfig(configFile);
}

But then you won't be able to provide mock configurations.

I know the 2nd opttion sounds more verbose and prone to error (if you forget to initialize). And it won't really hurt you that much if you do it in a constructor. But making your code more dependency-injection oriented is generally a good practice.

The 1st option is best - it can be used with both DI framework and with manual DI.


Singleton pattern

public class MyClass() {

    private static MyClass instance = null;
    /**
    * Get instance of my class, Singleton
    **/
    public static MyClass getInstance() {
        if(instance == null) {
            instance = new MyClass();
        }
        return instance;
    }
    /**
    * Private constructor
    */
    private MyClass() {
        //This will only be called once, by calling getInstanse() method. 
    }
}


Why not to use Static Initialization Blocks ? Additional details here: Static Initialization Blocks

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜